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