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 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 throw new LDAPException(ResultCode.LOCAL_ERROR,
1032 ERR_ADD_INTERRUPTED.get(connection.getHostPort()), ie);
1033 }
1034
1035 return handleResponse(connection, response, requestTime, depth, false);
1036 }
1037 finally
1038 {
1039 connection.deregisterResponseAcceptor(messageID);
1040 }
1041 }
1042
1043
1044
1045 /**
1046 * Sends this add request to the directory server over the provided connection
1047 * and returns the message ID for the request.
1048 *
1049 * @param connection The connection to use to communicate with the
1050 * directory server.
1051 * @param resultListener The async result listener that is to be notified
1052 * when the response is received. It may be
1053 * {@code null} only if the result is to be processed
1054 * by this class.
1055 *
1056 * @return The async request ID created for the operation, or {@code null} if
1057 * the provided {@code resultListener} is {@code null} and the
1058 * operation will not actually be processed asynchronously.
1059 *
1060 * @throws LDAPException If a problem occurs while sending the request.
1061 */
1062 AsyncRequestID processAsync(final LDAPConnection connection,
1063 final AsyncResultListener resultListener)
1064 throws LDAPException
1065 {
1066 // Create the LDAP message.
1067 messageID = connection.nextMessageID();
1068 final LDAPMessage message =
1069 new LDAPMessage(messageID, this, getControls());
1070
1071
1072 // If the provided async result listener is {@code null}, then we'll use
1073 // this class as the message acceptor. Otherwise, create an async helper
1074 // and use it as the message acceptor.
1075 final AsyncRequestID asyncRequestID;
1076 if (resultListener == null)
1077 {
1078 asyncRequestID = null;
1079 connection.registerResponseAcceptor(messageID, this);
1080 }
1081 else
1082 {
1083 final AsyncHelper helper = new AsyncHelper(connection, OperationType.ADD,
1084 messageID, resultListener, getIntermediateResponseListener());
1085 connection.registerResponseAcceptor(messageID, helper);
1086 asyncRequestID = helper.getAsyncRequestID();
1087
1088 final long timeout = getResponseTimeoutMillis(connection);
1089 if (timeout > 0L)
1090 {
1091 final Timer timer = connection.getTimer();
1092 final AsyncTimeoutTimerTask timerTask =
1093 new AsyncTimeoutTimerTask(helper);
1094 timer.schedule(timerTask, timeout);
1095 asyncRequestID.setTimerTask(timerTask);
1096 }
1097 }
1098
1099
1100 // Send the request to the server.
1101 try
1102 {
1103 debugLDAPRequest(this);
1104 connection.getConnectionStatistics().incrementNumAddRequests();
1105 connection.sendMessage(message);
1106 return asyncRequestID;
1107 }
1108 catch (LDAPException le)
1109 {
1110 debugException(le);
1111
1112 connection.deregisterResponseAcceptor(messageID);
1113 throw le;
1114 }
1115 }
1116
1117
1118
1119 /**
1120 * Processes this add operation in synchronous mode, in which the same thread
1121 * will send the request and read the response.
1122 *
1123 * @param connection The connection to use to communicate with the directory
1124 * server.
1125 * @param depth The current referral depth for this request. It should
1126 * always be one for the initial request, and should only
1127 * be incremented when following referrals.
1128 * @param allowRetry Indicates whether the request may be re-tried on a
1129 * re-established connection if the initial attempt fails
1130 * in a way that indicates the connection is no longer
1131 * valid and autoReconnect is true.
1132 *
1133 * @return An LDAP result object that provides information about the result
1134 * of the add processing.
1135 *
1136 * @throws LDAPException If a problem occurs while sending the request or
1137 * reading the response.
1138 */
1139 private LDAPResult processSync(final LDAPConnection connection,
1140 final int depth, final boolean allowRetry)
1141 throws LDAPException
1142 {
1143 // Create the LDAP message.
1144 messageID = connection.nextMessageID();
1145 final LDAPMessage message =
1146 new LDAPMessage(messageID, this, getControls());
1147
1148
1149 // Set the appropriate timeout on the socket.
1150 try
1151 {
1152 connection.getConnectionInternals(true).getSocket().setSoTimeout(
1153 (int) getResponseTimeoutMillis(connection));
1154 }
1155 catch (Exception e)
1156 {
1157 debugException(e);
1158 }
1159
1160
1161 // Send the request to the server.
1162 final long requestTime = System.nanoTime();
1163 debugLDAPRequest(this);
1164 connection.getConnectionStatistics().incrementNumAddRequests();
1165 try
1166 {
1167 connection.sendMessage(message);
1168 }
1169 catch (final LDAPException le)
1170 {
1171 debugException(le);
1172
1173 if (allowRetry)
1174 {
1175 final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1176 le.getResultCode());
1177 if (retryResult != null)
1178 {
1179 return retryResult;
1180 }
1181 }
1182
1183 throw le;
1184 }
1185
1186 while (true)
1187 {
1188 final LDAPResponse response;
1189 try
1190 {
1191 response = connection.readResponse(messageID);
1192 }
1193 catch (final LDAPException le)
1194 {
1195 debugException(le);
1196
1197 if ((le.getResultCode() == ResultCode.TIMEOUT) &&
1198 connection.getConnectionOptions().abandonOnTimeout())
1199 {
1200 connection.abandon(messageID);
1201 }
1202
1203 if (allowRetry)
1204 {
1205 final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1206 le.getResultCode());
1207 if (retryResult != null)
1208 {
1209 return retryResult;
1210 }
1211 }
1212
1213 throw le;
1214 }
1215
1216 if (response instanceof IntermediateResponse)
1217 {
1218 final IntermediateResponseListener listener =
1219 getIntermediateResponseListener();
1220 if (listener != null)
1221 {
1222 listener.intermediateResponseReturned(
1223 (IntermediateResponse) response);
1224 }
1225 }
1226 else
1227 {
1228 return handleResponse(connection, response, requestTime, depth,
1229 allowRetry);
1230 }
1231 }
1232 }
1233
1234
1235
1236 /**
1237 * Performs the necessary processing for handling a response.
1238 *
1239 * @param connection The connection used to read the response.
1240 * @param response The response to be processed.
1241 * @param requestTime The time the request was sent to the server.
1242 * @param depth The current referral depth for this request. It
1243 * should always be one for the initial request, and
1244 * should only be incremented when following referrals.
1245 * @param allowRetry Indicates whether the request may be re-tried on a
1246 * re-established connection if the initial attempt fails
1247 * in a way that indicates the connection is no longer
1248 * valid and autoReconnect is true.
1249 *
1250 * @return The add result.
1251 *
1252 * @throws LDAPException If a problem occurs.
1253 */
1254 private LDAPResult handleResponse(final LDAPConnection connection,
1255 final LDAPResponse response,
1256 final long requestTime, final int depth,
1257 final boolean allowRetry)
1258 throws LDAPException
1259 {
1260 if (response == null)
1261 {
1262 final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
1263 if (connection.getConnectionOptions().abandonOnTimeout())
1264 {
1265 connection.abandon(messageID);
1266 }
1267
1268 throw new LDAPException(ResultCode.TIMEOUT,
1269 ERR_ADD_CLIENT_TIMEOUT.get(waitTime, messageID, dn,
1270 connection.getHostPort()));
1271 }
1272
1273 connection.getConnectionStatistics().incrementNumAddResponses(
1274 System.nanoTime() - requestTime);
1275
1276 if (response instanceof ConnectionClosedResponse)
1277 {
1278 // The connection was closed while waiting for the response.
1279 if (allowRetry)
1280 {
1281 final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1282 ResultCode.SERVER_DOWN);
1283 if (retryResult != null)
1284 {
1285 return retryResult;
1286 }
1287 }
1288
1289 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
1290 final String message = ccr.getMessage();
1291 if (message == null)
1292 {
1293 throw new LDAPException(ccr.getResultCode(),
1294 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE.get(
1295 connection.getHostPort(), toString()));
1296 }
1297 else
1298 {
1299 throw new LDAPException(ccr.getResultCode(),
1300 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE_WITH_MESSAGE.get(
1301 connection.getHostPort(), toString(), message));
1302 }
1303 }
1304
1305 final LDAPResult result = (LDAPResult) response;
1306 if ((result.getResultCode().equals(ResultCode.REFERRAL)) &&
1307 followReferrals(connection))
1308 {
1309 if (depth >= connection.getConnectionOptions().getReferralHopLimit())
1310 {
1311 return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED,
1312 ERR_TOO_MANY_REFERRALS.get(),
1313 result.getMatchedDN(),
1314 result.getReferralURLs(),
1315 result.getResponseControls());
1316 }
1317
1318 return followReferral(result, connection, depth);
1319 }
1320 else
1321 {
1322 if (allowRetry)
1323 {
1324 final LDAPResult retryResult = reconnectAndRetry(connection, depth,
1325 result.getResultCode());
1326 if (retryResult != null)
1327 {
1328 return retryResult;
1329 }
1330 }
1331
1332 return result;
1333 }
1334 }
1335
1336
1337
1338 /**
1339 * Attempts to re-establish the connection and retry processing this request
1340 * on it.
1341 *
1342 * @param connection The connection to be re-established.
1343 * @param depth The current referral depth for this request. It should
1344 * always be one for the initial request, and should only
1345 * be incremented when following referrals.
1346 * @param resultCode The result code for the previous operation attempt.
1347 *
1348 * @return The result from re-trying the add, or {@code null} if it could not
1349 * be re-tried.
1350 */
1351 private LDAPResult reconnectAndRetry(final LDAPConnection connection,
1352 final int depth,
1353 final ResultCode resultCode)
1354 {
1355 try
1356 {
1357 // We will only want to retry for certain result codes that indicate a
1358 // connection problem.
1359 switch (resultCode.intValue())
1360 {
1361 case ResultCode.SERVER_DOWN_INT_VALUE:
1362 case ResultCode.DECODING_ERROR_INT_VALUE:
1363 case ResultCode.CONNECT_ERROR_INT_VALUE:
1364 connection.reconnect();
1365 return processSync(connection, depth, false);
1366 }
1367 }
1368 catch (final Exception e)
1369 {
1370 debugException(e);
1371 }
1372
1373 return null;
1374 }
1375
1376
1377
1378 /**
1379 * Attempts to follow a referral to perform an add operation in the target
1380 * server.
1381 *
1382 * @param referralResult The LDAP result object containing information about
1383 * the referral to follow.
1384 * @param connection The connection on which the referral was received.
1385 * @param depth The number of referrals followed in the course of
1386 * processing this request.
1387 *
1388 * @return The result of attempting to process the add operation by following
1389 * the referral.
1390 *
1391 * @throws LDAPException If a problem occurs while attempting to establish
1392 * the referral connection, sending the request, or
1393 * reading the result.
1394 */
1395 private LDAPResult followReferral(final LDAPResult referralResult,
1396 final LDAPConnection connection,
1397 final int depth)
1398 throws LDAPException
1399 {
1400 for (final String urlString : referralResult.getReferralURLs())
1401 {
1402 try
1403 {
1404 final LDAPURL referralURL = new LDAPURL(urlString);
1405 final String host = referralURL.getHost();
1406
1407 if (host == null)
1408 {
1409 // We can't handle a referral in which there is no host.
1410 continue;
1411 }
1412
1413 final AddRequest addRequest;
1414 if (referralURL.baseDNProvided())
1415 {
1416 addRequest = new AddRequest(referralURL.getBaseDN(), attributes,
1417 getControls());
1418 }
1419 else
1420 {
1421 addRequest = this;
1422 }
1423
1424 final LDAPConnection referralConn = connection.getReferralConnector().
1425 getReferralConnection(referralURL, connection);
1426 try
1427 {
1428 return addRequest.process(referralConn, (depth+1));
1429 }
1430 finally
1431 {
1432 referralConn.setDisconnectInfo(DisconnectType.REFERRAL, null, null);
1433 referralConn.close();
1434 }
1435 }
1436 catch (LDAPException le)
1437 {
1438 debugException(le);
1439 }
1440 }
1441
1442 // If we've gotten here, then we could not follow any of the referral URLs,
1443 // so we'll just return the original referral result.
1444 return referralResult;
1445 }
1446
1447
1448
1449 /**
1450 * {@inheritDoc}
1451 */
1452 @Override()
1453 public int getLastMessageID()
1454 {
1455 return messageID;
1456 }
1457
1458
1459
1460 /**
1461 * {@inheritDoc}
1462 */
1463 @Override()
1464 public OperationType getOperationType()
1465 {
1466 return OperationType.ADD;
1467 }
1468
1469
1470
1471 /**
1472 * {@inheritDoc}
1473 */
1474 public AddRequest duplicate()
1475 {
1476 return duplicate(getControls());
1477 }
1478
1479
1480
1481 /**
1482 * {@inheritDoc}
1483 */
1484 public AddRequest duplicate(final Control[] controls)
1485 {
1486 final ArrayList<Attribute> attrs = new ArrayList<Attribute>(attributes);
1487 final AddRequest r = new AddRequest(dn, attrs, controls);
1488
1489 if (followReferralsInternal() != null)
1490 {
1491 r.setFollowReferrals(followReferralsInternal());
1492 }
1493
1494 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
1495
1496 return r;
1497 }
1498
1499
1500
1501 /**
1502 * {@inheritDoc}
1503 */
1504 @InternalUseOnly()
1505 public void responseReceived(final LDAPResponse response)
1506 throws LDAPException
1507 {
1508 try
1509 {
1510 responseQueue.put(response);
1511 }
1512 catch (Exception e)
1513 {
1514 debugException(e);
1515 throw new LDAPException(ResultCode.LOCAL_ERROR,
1516 ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
1517 }
1518 }
1519
1520
1521
1522 /**
1523 * {@inheritDoc}
1524 */
1525 public LDIFAddChangeRecord toLDIFChangeRecord()
1526 {
1527 return new LDIFAddChangeRecord(this);
1528 }
1529
1530
1531
1532 /**
1533 * {@inheritDoc}
1534 */
1535 public String[] toLDIF()
1536 {
1537 return toLDIFChangeRecord().toLDIF();
1538 }
1539
1540
1541
1542 /**
1543 * {@inheritDoc}
1544 */
1545 public String toLDIFString()
1546 {
1547 return toLDIFChangeRecord().toLDIFString();
1548 }
1549
1550
1551
1552 /**
1553 * {@inheritDoc}
1554 */
1555 @Override()
1556 public void toString(final StringBuilder buffer)
1557 {
1558 buffer.append("AddRequest(dn='");
1559 buffer.append(dn);
1560 buffer.append("', attrs={");
1561
1562 for (int i=0; i < attributes.size(); i++)
1563 {
1564 if (i > 0)
1565 {
1566 buffer.append(", ");
1567 }
1568
1569 buffer.append(attributes.get(i));
1570 }
1571 buffer.append('}');
1572
1573 final Control[] controls = getControls();
1574 if (controls.length > 0)
1575 {
1576 buffer.append(", controls={");
1577 for (int i=0; i < controls.length; i++)
1578 {
1579 if (i > 0)
1580 {
1581 buffer.append(", ");
1582 }
1583
1584 buffer.append(controls[i]);
1585 }
1586 buffer.append('}');
1587 }
1588
1589 buffer.append(')');
1590 }
1591
1592
1593
1594 /**
1595 * {@inheritDoc}
1596 */
1597 public void toCode(final List<String> lineList, final String requestID,
1598 final int indentSpaces, final boolean includeProcessing)
1599 {
1600 // Create the request variable.
1601 final ArrayList<ToCodeArgHelper> constructorArgs =
1602 new ArrayList<ToCodeArgHelper>(attributes.size() + 1);
1603 constructorArgs.add(ToCodeArgHelper.createString(dn, "Entry DN"));
1604
1605 boolean firstAttribute = true;
1606 for (final Attribute a : attributes)
1607 {
1608 final String comment;
1609 if (firstAttribute)
1610 {
1611 firstAttribute = false;
1612 comment = "Entry Attributes";
1613 }
1614 else
1615 {
1616 comment = null;
1617 }
1618
1619 constructorArgs.add(ToCodeArgHelper.createAttribute(a, comment));
1620 }
1621
1622 ToCodeHelper.generateMethodCall(lineList, indentSpaces, "AddRequest",
1623 requestID + "Request", "new AddRequest", constructorArgs);
1624
1625
1626 // If there are any controls, then add them to the request.
1627 for (final Control c : getControls())
1628 {
1629 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null,
1630 requestID + "Request.addControl",
1631 ToCodeArgHelper.createControl(c, null));
1632 }
1633
1634
1635 // Add lines for processing the request and obtaining the result.
1636 if (includeProcessing)
1637 {
1638 // Generate a string with the appropriate indent.
1639 final StringBuilder buffer = new StringBuilder();
1640 for (int i=0; i < indentSpaces; i++)
1641 {
1642 buffer.append(' ');
1643 }
1644 final String indent = buffer.toString();
1645
1646 lineList.add("");
1647 lineList.add(indent + "try");
1648 lineList.add(indent + '{');
1649 lineList.add(indent + " LDAPResult " + requestID +
1650 "Result = connection.add(" + requestID + "Request);");
1651 lineList.add(indent + " // The add was processed successfully.");
1652 lineList.add(indent + '}');
1653 lineList.add(indent + "catch (LDAPException e)");
1654 lineList.add(indent + '{');
1655 lineList.add(indent + " // The add failed. Maybe the following will " +
1656 "help explain why.");
1657 lineList.add(indent + " ResultCode resultCode = e.getResultCode();");
1658 lineList.add(indent + " String message = e.getMessage();");
1659 lineList.add(indent + " String matchedDN = e.getMatchedDN();");
1660 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();");
1661 lineList.add(indent + " Control[] responseControls = " +
1662 "e.getResponseControls();");
1663 lineList.add(indent + '}');
1664 }
1665 }
1666 }