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