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