001    /*
002     * Copyright 2007-2012 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2012 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      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        }
984    
985        final long requestTime = System.nanoTime();
986        processAsync(connection, null);
987    
988        try
989        {
990          // Wait for and process the response.
991          final LDAPResponse response;
992          try
993          {
994            final long responseTimeout = getResponseTimeoutMillis(connection);
995            if (responseTimeout > 0)
996            {
997              response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
998            }
999            else
1000            {
1001              response = responseQueue.take();
1002            }
1003          }
1004          catch (InterruptedException ie)
1005          {
1006            debugException(ie);
1007            throw new LDAPException(ResultCode.LOCAL_ERROR,
1008                 ERR_ADD_INTERRUPTED.get(connection.getHostPort()), ie);
1009          }
1010    
1011          return handleResponse(connection, response, requestTime, depth);
1012        }
1013        finally
1014        {
1015          connection.deregisterResponseAcceptor(messageID);
1016        }
1017      }
1018    
1019    
1020    
1021      /**
1022       * Sends this add request to the directory server over the provided connection
1023       * and returns the message ID for the request.
1024       *
1025       * @param  connection      The connection to use to communicate with the
1026       *                         directory server.
1027       * @param  resultListener  The async result listener that is to be notified
1028       *                         when the response is received.  It may be
1029       *                         {@code null} only if the result is to be processed
1030       *                         by this class.
1031       *
1032       * @return  The async request ID created for the operation, or {@code null} if
1033       *          the provided {@code resultListener} is {@code null} and the
1034       *          operation will not actually be processed asynchronously.
1035       *
1036       * @throws  LDAPException  If a problem occurs while sending the request.
1037       */
1038      AsyncRequestID processAsync(final LDAPConnection connection,
1039                                  final AsyncResultListener resultListener)
1040                     throws LDAPException
1041      {
1042        // Create the LDAP message.
1043        messageID = connection.nextMessageID();
1044        final LDAPMessage message =
1045             new LDAPMessage(messageID,  this, getControls());
1046    
1047    
1048        // If the provided async result listener is {@code null}, then we'll use
1049        // this class as the message acceptor.  Otherwise, create an async helper
1050        // and use it as the message acceptor.
1051        final AsyncRequestID asyncRequestID;
1052        if (resultListener == null)
1053        {
1054          asyncRequestID = null;
1055          connection.registerResponseAcceptor(messageID, this);
1056        }
1057        else
1058        {
1059          final AsyncHelper helper = new AsyncHelper(connection, OperationType.ADD,
1060               messageID, resultListener, getIntermediateResponseListener());
1061          connection.registerResponseAcceptor(messageID, helper);
1062          asyncRequestID = helper.getAsyncRequestID();
1063    
1064          final long timeout = getResponseTimeoutMillis(connection);
1065          if (timeout > 0L)
1066          {
1067            final Timer timer = connection.getTimer();
1068            final AsyncTimeoutTimerTask timerTask =
1069                 new AsyncTimeoutTimerTask(helper);
1070            timer.schedule(timerTask, timeout);
1071            asyncRequestID.setTimerTask(timerTask);
1072          }
1073        }
1074    
1075    
1076        // Send the request to the server.
1077        try
1078        {
1079          debugLDAPRequest(this);
1080          connection.getConnectionStatistics().incrementNumAddRequests();
1081          connection.sendMessage(message);
1082          return asyncRequestID;
1083        }
1084        catch (LDAPException le)
1085        {
1086          debugException(le);
1087    
1088          connection.deregisterResponseAcceptor(messageID);
1089          throw le;
1090        }
1091      }
1092    
1093    
1094    
1095      /**
1096       * Processes this add operation in synchronous mode, in which the same thread
1097       * will send the request and read the response.
1098       *
1099       * @param  connection  The connection to use to communicate with the directory
1100       *                     server.
1101       * @param  depth       The current referral depth for this request.  It should
1102       *                     always be one for the initial request, and should only
1103       *                     be incremented when following referrals.
1104       *
1105       * @return  An LDAP result object that provides information about the result
1106       *          of the add processing.
1107       *
1108       * @throws  LDAPException  If a problem occurs while sending the request or
1109       *                         reading the response.
1110       */
1111      private LDAPResult processSync(final LDAPConnection connection,
1112                                     final int depth)
1113              throws LDAPException
1114      {
1115        // Create the LDAP message.
1116        messageID = connection.nextMessageID();
1117        final LDAPMessage message =
1118             new LDAPMessage(messageID,  this, getControls());
1119    
1120    
1121        // Set the appropriate timeout on the socket.
1122        try
1123        {
1124          connection.getConnectionInternals().getSocket().setSoTimeout(
1125               (int) getResponseTimeoutMillis(connection));
1126        }
1127        catch (Exception e)
1128        {
1129          debugException(e);
1130        }
1131    
1132    
1133        // Send the request to the server.
1134        final long requestTime = System.nanoTime();
1135        debugLDAPRequest(this);
1136        connection.getConnectionStatistics().incrementNumAddRequests();
1137        connection.sendMessage(message);
1138    
1139        while (true)
1140        {
1141          final LDAPResponse response;
1142          try
1143          {
1144            response = connection.readResponse(messageID);
1145          }
1146          catch (final LDAPException le)
1147          {
1148            debugException(le);
1149    
1150            if ((le.getResultCode() == ResultCode.TIMEOUT) &&
1151                connection.getConnectionOptions().abandonOnTimeout())
1152            {
1153              connection.abandon(messageID);
1154            }
1155    
1156            throw le;
1157          }
1158    
1159          if (response instanceof IntermediateResponse)
1160          {
1161            final IntermediateResponseListener listener =
1162                 getIntermediateResponseListener();
1163            if (listener != null)
1164            {
1165              listener.intermediateResponseReturned(
1166                   (IntermediateResponse) response);
1167            }
1168          }
1169          else
1170          {
1171            return handleResponse(connection, response, requestTime, depth);
1172          }
1173        }
1174      }
1175    
1176    
1177    
1178      /**
1179       * Performs the necessary processing for handling a response.
1180       *
1181       * @param  connection   The connection used to read the response.
1182       * @param  response     The response to be processed.
1183       * @param  requestTime  The time the request was sent to the server.
1184       * @param  depth        The current referral depth for this request.  It
1185       *                      should always be one for the initial request, and
1186       *                      should only be incremented when following referrals.
1187       *
1188       * @return  The add result.
1189       *
1190       * @throws  LDAPException  If a problem occurs.
1191       */
1192      private LDAPResult handleResponse(final LDAPConnection connection,
1193                                        final LDAPResponse response,
1194                                        final long requestTime, final int depth)
1195              throws LDAPException
1196      {
1197        if (response == null)
1198        {
1199          final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
1200          if (connection.getConnectionOptions().abandonOnTimeout())
1201          {
1202            connection.abandon(messageID);
1203          }
1204    
1205          throw new LDAPException(ResultCode.TIMEOUT,
1206               ERR_ADD_CLIENT_TIMEOUT.get(waitTime, connection.getHostPort()));
1207        }
1208    
1209        connection.getConnectionStatistics().incrementNumAddResponses(
1210             System.nanoTime() - requestTime);
1211    
1212        if (response instanceof ConnectionClosedResponse)
1213        {
1214          // The connection was closed while waiting for the response.
1215          final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
1216          final String message = ccr.getMessage();
1217          if (message == null)
1218          {
1219            throw new LDAPException(ccr.getResultCode(),
1220                 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE.get(
1221                      connection.getHostPort(), toString()));
1222          }
1223          else
1224          {
1225            throw new LDAPException(ccr.getResultCode(),
1226                 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE_WITH_MESSAGE.get(
1227                      connection.getHostPort(), toString(), message));
1228          }
1229        }
1230    
1231        final LDAPResult result = (LDAPResult) response;
1232        if ((result.getResultCode().equals(ResultCode.REFERRAL)) &&
1233            followReferrals(connection))
1234        {
1235          if (depth >= connection.getConnectionOptions().getReferralHopLimit())
1236          {
1237            return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED,
1238                                  ERR_TOO_MANY_REFERRALS.get(),
1239                                  result.getMatchedDN(),
1240                                  result.getReferralURLs(),
1241                                  result.getResponseControls());
1242          }
1243    
1244          return followReferral(result, connection, depth);
1245        }
1246        else
1247        {
1248          return result;
1249        }
1250      }
1251    
1252    
1253    
1254      /**
1255       * Attempts to follow a referral to perform an add operation in the target
1256       * server.
1257       *
1258       * @param  referralResult  The LDAP result object containing information about
1259       *                         the referral to follow.
1260       * @param  connection      The connection on which the referral was received.
1261       * @param  depth           The number of referrals followed in the course of
1262       *                         processing this request.
1263       *
1264       * @return  The result of attempting to process the add operation by following
1265       *          the referral.
1266       *
1267       * @throws  LDAPException  If a problem occurs while attempting to establish
1268       *                         the referral connection, sending the request, or
1269       *                         reading the result.
1270       */
1271      private LDAPResult followReferral(final LDAPResult referralResult,
1272                                        final LDAPConnection connection,
1273                                        final int depth)
1274              throws LDAPException
1275      {
1276        for (final String urlString : referralResult.getReferralURLs())
1277        {
1278          try
1279          {
1280            final LDAPURL referralURL = new LDAPURL(urlString);
1281            final String host = referralURL.getHost();
1282    
1283            if (host == null)
1284            {
1285              // We can't handle a referral in which there is no host.
1286              continue;
1287            }
1288    
1289            final AddRequest addRequest;
1290            if (referralURL.baseDNProvided())
1291            {
1292              addRequest = new AddRequest(referralURL.getBaseDN(), attributes,
1293                                          getControls());
1294            }
1295            else
1296            {
1297              addRequest = this;
1298            }
1299    
1300            final LDAPConnection referralConn = connection.getReferralConnector().
1301                 getReferralConnection(referralURL, connection);
1302            try
1303            {
1304              return addRequest.process(referralConn, (depth+1));
1305            }
1306            finally
1307            {
1308              referralConn.setDisconnectInfo(DisconnectType.REFERRAL, null, null);
1309              referralConn.close();
1310            }
1311          }
1312          catch (LDAPException le)
1313          {
1314            debugException(le);
1315          }
1316        }
1317    
1318        // If we've gotten here, then we could not follow any of the referral URLs,
1319        // so we'll just return the original referral result.
1320        return referralResult;
1321      }
1322    
1323    
1324    
1325      /**
1326       * {@inheritDoc}
1327       */
1328      @Override()
1329      public int getLastMessageID()
1330      {
1331        return messageID;
1332      }
1333    
1334    
1335    
1336      /**
1337       * {@inheritDoc}
1338       */
1339      @Override()
1340      public OperationType getOperationType()
1341      {
1342        return OperationType.ADD;
1343      }
1344    
1345    
1346    
1347      /**
1348       * {@inheritDoc}
1349       */
1350      public AddRequest duplicate()
1351      {
1352        return duplicate(getControls());
1353      }
1354    
1355    
1356    
1357      /**
1358       * {@inheritDoc}
1359       */
1360      public AddRequest duplicate(final Control[] controls)
1361      {
1362        final ArrayList<Attribute> attrs = new ArrayList<Attribute>(attributes);
1363        final AddRequest r = new AddRequest(dn, attrs, controls);
1364    
1365        if (followReferralsInternal() != null)
1366        {
1367          r.setFollowReferrals(followReferralsInternal());
1368        }
1369    
1370        r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
1371    
1372        return r;
1373      }
1374    
1375    
1376    
1377      /**
1378       * {@inheritDoc}
1379       */
1380      @InternalUseOnly()
1381      public void responseReceived(final LDAPResponse response)
1382             throws LDAPException
1383      {
1384        try
1385        {
1386          responseQueue.put(response);
1387        }
1388        catch (Exception e)
1389        {
1390          debugException(e);
1391          throw new LDAPException(ResultCode.LOCAL_ERROR,
1392               ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
1393        }
1394      }
1395    
1396    
1397    
1398      /**
1399       * {@inheritDoc}
1400       */
1401      public LDIFAddChangeRecord toLDIFChangeRecord()
1402      {
1403        return new LDIFAddChangeRecord(this);
1404      }
1405    
1406    
1407    
1408      /**
1409       * {@inheritDoc}
1410       */
1411      public String[] toLDIF()
1412      {
1413        return toLDIFChangeRecord().toLDIF();
1414      }
1415    
1416    
1417    
1418      /**
1419       * {@inheritDoc}
1420       */
1421      public String toLDIFString()
1422      {
1423        return toLDIFChangeRecord().toLDIFString();
1424      }
1425    
1426    
1427    
1428      /**
1429       * {@inheritDoc}
1430       */
1431      @Override()
1432      public void toString(final StringBuilder buffer)
1433      {
1434        buffer.append("AddRequest(dn='");
1435        buffer.append(dn);
1436        buffer.append("', attrs={");
1437    
1438        for (int i=0; i < attributes.size(); i++)
1439        {
1440          if (i > 0)
1441          {
1442            buffer.append(", ");
1443          }
1444    
1445          buffer.append(attributes.get(i));
1446        }
1447        buffer.append('}');
1448    
1449        final Control[] controls = getControls();
1450        if (controls.length > 0)
1451        {
1452          buffer.append(", controls={");
1453          for (int i=0; i < controls.length; i++)
1454          {
1455            if (i > 0)
1456            {
1457              buffer.append(", ");
1458            }
1459    
1460            buffer.append(controls[i]);
1461          }
1462          buffer.append('}');
1463        }
1464    
1465        buffer.append(')');
1466      }
1467    }