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