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.Debug; 050import com.unboundid.util.InternalUseOnly; 051import com.unboundid.util.Mutable; 052import com.unboundid.util.StaticUtils; 053import com.unboundid.util.ThreadSafety; 054import com.unboundid.util.ThreadSafetyLevel; 055import com.unboundid.util.Validator; 056 057import static com.unboundid.ldap.sdk.LDAPMessages.*; 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<>(); 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 Validator.ensureNotNull(dn, attributes); 127 128 this.dn = dn; 129 130 this.attributes = new ArrayList<>(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 Validator.ensureNotNull(dn, attributes); 151 152 this.dn = dn; 153 154 this.attributes = new ArrayList<>(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 Validator.ensureNotNull(dn, attributes); 173 174 this.dn = dn; 175 this.attributes = new ArrayList<>(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 Validator.ensureNotNull(dn, attributes); 195 196 this.dn = dn; 197 this.attributes = new ArrayList<>(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 Validator.ensureNotNull(dn, attributes); 215 216 this.dn = dn.toString(); 217 218 this.attributes = new ArrayList<>(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 Validator.ensureNotNull(dn, attributes); 239 240 this.dn = dn.toString(); 241 242 this.attributes = new ArrayList<>(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 Validator.ensureNotNull(dn, attributes); 261 262 this.dn = dn.toString(); 263 this.attributes = new ArrayList<>(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 Validator.ensureNotNull(dn, attributes); 283 284 this.dn = dn.toString(); 285 this.attributes = new ArrayList<>(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 Validator.ensureNotNull(entry); 300 301 dn = entry.getDN(); 302 attributes = new ArrayList<>(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 Validator.ensureNotNull(entry); 318 319 dn = entry.getDN(); 320 attributes = new ArrayList<>(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<>(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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Validator.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 Debug.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 Debug.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 Debug.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 Debug.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 Debug.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 Debug.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 = 1267 StaticUtils.nanosToMillis(System.nanoTime() - requestTime); 1268 if (connection.getConnectionOptions().abandonOnTimeout()) 1269 { 1270 connection.abandon(messageID); 1271 } 1272 1273 throw new LDAPException(ResultCode.TIMEOUT, 1274 ERR_ADD_CLIENT_TIMEOUT.get(waitTime, messageID, dn, 1275 connection.getHostPort())); 1276 } 1277 1278 connection.getConnectionStatistics().incrementNumAddResponses( 1279 System.nanoTime() - requestTime); 1280 1281 if (response instanceof ConnectionClosedResponse) 1282 { 1283 // The connection was closed while waiting for the response. 1284 if (allowRetry) 1285 { 1286 final LDAPResult retryResult = reconnectAndRetry(connection, depth, 1287 ResultCode.SERVER_DOWN); 1288 if (retryResult != null) 1289 { 1290 return retryResult; 1291 } 1292 } 1293 1294 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response; 1295 final String message = ccr.getMessage(); 1296 if (message == null) 1297 { 1298 throw new LDAPException(ccr.getResultCode(), 1299 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE.get( 1300 connection.getHostPort(), toString())); 1301 } 1302 else 1303 { 1304 throw new LDAPException(ccr.getResultCode(), 1305 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE_WITH_MESSAGE.get( 1306 connection.getHostPort(), toString(), message)); 1307 } 1308 } 1309 1310 final LDAPResult result = (LDAPResult) response; 1311 if ((result.getResultCode().equals(ResultCode.REFERRAL)) && 1312 followReferrals(connection)) 1313 { 1314 if (depth >= connection.getConnectionOptions().getReferralHopLimit()) 1315 { 1316 return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED, 1317 ERR_TOO_MANY_REFERRALS.get(), 1318 result.getMatchedDN(), 1319 result.getReferralURLs(), 1320 result.getResponseControls()); 1321 } 1322 1323 return followReferral(result, connection, depth); 1324 } 1325 else 1326 { 1327 if (allowRetry) 1328 { 1329 final LDAPResult retryResult = reconnectAndRetry(connection, depth, 1330 result.getResultCode()); 1331 if (retryResult != null) 1332 { 1333 return retryResult; 1334 } 1335 } 1336 1337 return result; 1338 } 1339 } 1340 1341 1342 1343 /** 1344 * Attempts to re-establish the connection and retry processing this request 1345 * on it. 1346 * 1347 * @param connection The connection to be re-established. 1348 * @param depth The current referral depth for this request. It should 1349 * always be one for the initial request, and should only 1350 * be incremented when following referrals. 1351 * @param resultCode The result code for the previous operation attempt. 1352 * 1353 * @return The result from re-trying the add, or {@code null} if it could not 1354 * be re-tried. 1355 */ 1356 private LDAPResult reconnectAndRetry(final LDAPConnection connection, 1357 final int depth, 1358 final ResultCode resultCode) 1359 { 1360 try 1361 { 1362 // We will only want to retry for certain result codes that indicate a 1363 // connection problem. 1364 switch (resultCode.intValue()) 1365 { 1366 case ResultCode.SERVER_DOWN_INT_VALUE: 1367 case ResultCode.DECODING_ERROR_INT_VALUE: 1368 case ResultCode.CONNECT_ERROR_INT_VALUE: 1369 connection.reconnect(); 1370 return processSync(connection, depth, false); 1371 } 1372 } 1373 catch (final Exception e) 1374 { 1375 Debug.debugException(e); 1376 } 1377 1378 return null; 1379 } 1380 1381 1382 1383 /** 1384 * Attempts to follow a referral to perform an add operation in the target 1385 * server. 1386 * 1387 * @param referralResult The LDAP result object containing information about 1388 * the referral to follow. 1389 * @param connection The connection on which the referral was received. 1390 * @param depth The number of referrals followed in the course of 1391 * processing this request. 1392 * 1393 * @return The result of attempting to process the add operation by following 1394 * the referral. 1395 * 1396 * @throws LDAPException If a problem occurs while attempting to establish 1397 * the referral connection, sending the request, or 1398 * reading the result. 1399 */ 1400 private LDAPResult followReferral(final LDAPResult referralResult, 1401 final LDAPConnection connection, 1402 final int depth) 1403 throws LDAPException 1404 { 1405 for (final String urlString : referralResult.getReferralURLs()) 1406 { 1407 try 1408 { 1409 final LDAPURL referralURL = new LDAPURL(urlString); 1410 final String host = referralURL.getHost(); 1411 1412 if (host == null) 1413 { 1414 // We can't handle a referral in which there is no host. 1415 continue; 1416 } 1417 1418 final AddRequest addRequest; 1419 if (referralURL.baseDNProvided()) 1420 { 1421 addRequest = new AddRequest(referralURL.getBaseDN(), attributes, 1422 getControls()); 1423 } 1424 else 1425 { 1426 addRequest = this; 1427 } 1428 1429 final LDAPConnection referralConn = getReferralConnector(connection). 1430 getReferralConnection(referralURL, connection); 1431 try 1432 { 1433 return addRequest.process(referralConn, (depth+1)); 1434 } 1435 finally 1436 { 1437 referralConn.setDisconnectInfo(DisconnectType.REFERRAL, null, null); 1438 referralConn.close(); 1439 } 1440 } 1441 catch (final LDAPException le) 1442 { 1443 Debug.debugException(le); 1444 } 1445 } 1446 1447 // If we've gotten here, then we could not follow any of the referral URLs, 1448 // so we'll just return the original referral result. 1449 return referralResult; 1450 } 1451 1452 1453 1454 /** 1455 * {@inheritDoc} 1456 */ 1457 @Override() 1458 public int getLastMessageID() 1459 { 1460 return messageID; 1461 } 1462 1463 1464 1465 /** 1466 * {@inheritDoc} 1467 */ 1468 @Override() 1469 public OperationType getOperationType() 1470 { 1471 return OperationType.ADD; 1472 } 1473 1474 1475 1476 /** 1477 * {@inheritDoc} 1478 */ 1479 @Override() 1480 public AddRequest duplicate() 1481 { 1482 return duplicate(getControls()); 1483 } 1484 1485 1486 1487 /** 1488 * {@inheritDoc} 1489 */ 1490 @Override() 1491 public AddRequest duplicate(final Control[] controls) 1492 { 1493 final ArrayList<Attribute> attrs = new ArrayList<>(attributes); 1494 final AddRequest r = new AddRequest(dn, attrs, controls); 1495 1496 if (followReferralsInternal() != null) 1497 { 1498 r.setFollowReferrals(followReferralsInternal()); 1499 } 1500 1501 if (getReferralConnectorInternal() != null) 1502 { 1503 r.setReferralConnector(getReferralConnectorInternal()); 1504 } 1505 1506 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 1507 1508 return r; 1509 } 1510 1511 1512 1513 /** 1514 * {@inheritDoc} 1515 */ 1516 @InternalUseOnly() 1517 @Override() 1518 public void responseReceived(final LDAPResponse response) 1519 throws LDAPException 1520 { 1521 try 1522 { 1523 responseQueue.put(response); 1524 } 1525 catch (final Exception e) 1526 { 1527 Debug.debugException(e); 1528 1529 if (e instanceof InterruptedException) 1530 { 1531 Thread.currentThread().interrupt(); 1532 } 1533 1534 throw new LDAPException(ResultCode.LOCAL_ERROR, 1535 ERR_EXCEPTION_HANDLING_RESPONSE.get( 1536 StaticUtils.getExceptionMessage(e)), 1537 e); 1538 } 1539 } 1540 1541 1542 1543 /** 1544 * {@inheritDoc} 1545 */ 1546 @Override() 1547 public LDIFAddChangeRecord toLDIFChangeRecord() 1548 { 1549 return new LDIFAddChangeRecord(this); 1550 } 1551 1552 1553 1554 /** 1555 * {@inheritDoc} 1556 */ 1557 @Override() 1558 public String[] toLDIF() 1559 { 1560 return toLDIFChangeRecord().toLDIF(); 1561 } 1562 1563 1564 1565 /** 1566 * {@inheritDoc} 1567 */ 1568 @Override() 1569 public String toLDIFString() 1570 { 1571 return toLDIFChangeRecord().toLDIFString(); 1572 } 1573 1574 1575 1576 /** 1577 * {@inheritDoc} 1578 */ 1579 @Override() 1580 public void toString(final StringBuilder buffer) 1581 { 1582 buffer.append("AddRequest(dn='"); 1583 buffer.append(dn); 1584 buffer.append("', attrs={"); 1585 1586 for (int i=0; i < attributes.size(); i++) 1587 { 1588 if (i > 0) 1589 { 1590 buffer.append(", "); 1591 } 1592 1593 buffer.append(attributes.get(i)); 1594 } 1595 buffer.append('}'); 1596 1597 final Control[] controls = getControls(); 1598 if (controls.length > 0) 1599 { 1600 buffer.append(", controls={"); 1601 for (int i=0; i < controls.length; i++) 1602 { 1603 if (i > 0) 1604 { 1605 buffer.append(", "); 1606 } 1607 1608 buffer.append(controls[i]); 1609 } 1610 buffer.append('}'); 1611 } 1612 1613 buffer.append(')'); 1614 } 1615 1616 1617 1618 /** 1619 * {@inheritDoc} 1620 */ 1621 @Override() 1622 public void toCode(final List<String> lineList, final String requestID, 1623 final int indentSpaces, final boolean includeProcessing) 1624 { 1625 // Create the request variable. 1626 final ArrayList<ToCodeArgHelper> constructorArgs = 1627 new ArrayList<>(attributes.size() + 1); 1628 constructorArgs.add(ToCodeArgHelper.createString(dn, "Entry DN")); 1629 1630 boolean firstAttribute = true; 1631 for (final Attribute a : attributes) 1632 { 1633 final String comment; 1634 if (firstAttribute) 1635 { 1636 firstAttribute = false; 1637 comment = "Entry Attributes"; 1638 } 1639 else 1640 { 1641 comment = null; 1642 } 1643 1644 constructorArgs.add(ToCodeArgHelper.createAttribute(a, comment)); 1645 } 1646 1647 ToCodeHelper.generateMethodCall(lineList, indentSpaces, "AddRequest", 1648 requestID + "Request", "new AddRequest", constructorArgs); 1649 1650 1651 // If there are any controls, then add them to the request. 1652 for (final Control c : getControls()) 1653 { 1654 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1655 requestID + "Request.addControl", 1656 ToCodeArgHelper.createControl(c, null)); 1657 } 1658 1659 1660 // Add lines for processing the request and obtaining the result. 1661 if (includeProcessing) 1662 { 1663 // Generate a string with the appropriate indent. 1664 final StringBuilder buffer = new StringBuilder(); 1665 for (int i=0; i < indentSpaces; i++) 1666 { 1667 buffer.append(' '); 1668 } 1669 final String indent = buffer.toString(); 1670 1671 lineList.add(""); 1672 lineList.add(indent + "try"); 1673 lineList.add(indent + '{'); 1674 lineList.add(indent + " LDAPResult " + requestID + 1675 "Result = connection.add(" + requestID + "Request);"); 1676 lineList.add(indent + " // The add was processed successfully."); 1677 lineList.add(indent + '}'); 1678 lineList.add(indent + "catch (LDAPException e)"); 1679 lineList.add(indent + '{'); 1680 lineList.add(indent + " // The add failed. Maybe the following will " + 1681 "help explain why."); 1682 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 1683 lineList.add(indent + " String message = e.getMessage();"); 1684 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 1685 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 1686 lineList.add(indent + " Control[] responseControls = " + 1687 "e.getResponseControls();"); 1688 lineList.add(indent + '}'); 1689 } 1690 } 1691}