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