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