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