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 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 }