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