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