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