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