001/* 002 * Copyright 2007-2026 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2007-2026 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-2026 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 return processSync(connection, depth); 1063 } 1064 1065 final long requestTime = System.nanoTime(); 1066 processAsync(connection, null); 1067 1068 try 1069 { 1070 // Wait for and process the response. 1071 final LDAPResponse response; 1072 try 1073 { 1074 final long responseTimeout = getResponseTimeoutMillis(connection); 1075 if (responseTimeout > 0) 1076 { 1077 response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS); 1078 } 1079 else 1080 { 1081 response = responseQueue.take(); 1082 } 1083 } 1084 catch (final InterruptedException ie) 1085 { 1086 Debug.debugException(ie); 1087 Thread.currentThread().interrupt(); 1088 throw new LDAPException(ResultCode.LOCAL_ERROR, 1089 ERR_ADD_INTERRUPTED.get(connection.getHostPort()), ie); 1090 } 1091 1092 return handleResponse(connection, response, requestTime, depth); 1093 } 1094 finally 1095 { 1096 connection.deregisterResponseAcceptor(messageID); 1097 } 1098 } 1099 1100 1101 1102 /** 1103 * Sends this add request to the directory server over the provided connection 1104 * and returns the message ID for the request. 1105 * 1106 * @param connection The connection to use to communicate with the 1107 * directory server. 1108 * @param resultListener The async result listener that is to be notified 1109 * when the response is received. It may be 1110 * {@code null} only if the result is to be processed 1111 * by this class. 1112 * 1113 * @return The async request ID created for the operation, or {@code null} if 1114 * the provided {@code resultListener} is {@code null} and the 1115 * operation will not actually be processed asynchronously. 1116 * 1117 * @throws LDAPException If a problem occurs while sending the request. 1118 */ 1119 @NotNull() 1120 AsyncRequestID processAsync(@NotNull final LDAPConnection connection, 1121 @Nullable final AsyncResultListener resultListener) 1122 throws LDAPException 1123 { 1124 // Create the LDAP message. 1125 messageID = connection.nextMessageID(); 1126 final LDAPMessage message = 1127 new LDAPMessage(messageID, this, getControls()); 1128 1129 1130 // If the provided async result listener is {@code null}, then we'll use 1131 // this class as the message acceptor. Otherwise, create an async helper 1132 // and use it as the message acceptor. 1133 final AsyncRequestID asyncRequestID; 1134 final long timeout = getResponseTimeoutMillis(connection); 1135 if (resultListener == null) 1136 { 1137 asyncRequestID = null; 1138 connection.registerResponseAcceptor(messageID, this); 1139 } 1140 else 1141 { 1142 final AsyncHelper helper = new AsyncHelper(connection, OperationType.ADD, 1143 messageID, resultListener, getIntermediateResponseListener()); 1144 connection.registerResponseAcceptor(messageID, helper); 1145 asyncRequestID = helper.getAsyncRequestID(); 1146 1147 if (timeout > 0L) 1148 { 1149 final Timer timer = connection.getTimer(); 1150 final AsyncTimeoutTimerTask timerTask = 1151 new AsyncTimeoutTimerTask(helper); 1152 timer.schedule(timerTask, timeout); 1153 asyncRequestID.setTimerTask(timerTask); 1154 } 1155 } 1156 1157 1158 // Send the request to the server. 1159 try 1160 { 1161 Debug.debugLDAPRequest(Level.INFO, this, messageID, connection); 1162 1163 final LDAPConnectionLogger logger = 1164 connection.getConnectionOptions().getConnectionLogger(); 1165 if (logger != null) 1166 { 1167 logger.logAddRequest(connection, messageID, this); 1168 } 1169 1170 connection.getConnectionStatistics().incrementNumAddRequests(); 1171 connection.sendMessage(message, timeout); 1172 return asyncRequestID; 1173 } 1174 catch (final LDAPException le) 1175 { 1176 Debug.debugException(le); 1177 1178 connection.deregisterResponseAcceptor(messageID); 1179 throw le; 1180 } 1181 } 1182 1183 1184 1185 /** 1186 * Processes this add operation in synchronous mode, in which the same thread 1187 * will send the request and read the response. 1188 * 1189 * @param connection The connection to use to communicate with the directory 1190 * server. 1191 * @param depth The current referral depth for this request. It should 1192 * always be one for the initial request, and should only 1193 * be incremented when following referrals. 1194 * 1195 * @return An LDAP result object that provides information about the result 1196 * of the add processing. 1197 * 1198 * @throws LDAPException If a problem occurs while sending the request or 1199 * reading the response. 1200 */ 1201 @NotNull() 1202 private LDAPResult processSync(@NotNull final LDAPConnection connection, 1203 final int depth) 1204 throws LDAPException 1205 { 1206 // Create the LDAP message. 1207 messageID = connection.nextMessageID(); 1208 final LDAPMessage message = 1209 new LDAPMessage(messageID, this, getControls()); 1210 1211 1212 // Send the request to the server. 1213 final long requestTime = System.nanoTime(); 1214 Debug.debugLDAPRequest(Level.INFO, this, messageID, connection); 1215 1216 final LDAPConnectionLogger logger = 1217 connection.getConnectionOptions().getConnectionLogger(); 1218 if (logger != null) 1219 { 1220 logger.logAddRequest(connection, messageID, this); 1221 } 1222 1223 connection.getConnectionStatistics().incrementNumAddRequests(); 1224 connection.sendMessage(message, getResponseTimeoutMillis(connection)); 1225 1226 while (true) 1227 { 1228 final LDAPResponse response; 1229 try 1230 { 1231 response = connection.readResponse(messageID); 1232 } 1233 catch (final LDAPException le) 1234 { 1235 Debug.debugException(le); 1236 1237 if ((le.getResultCode() == ResultCode.TIMEOUT) && 1238 connection.getConnectionOptions().abandonOnTimeout()) 1239 { 1240 connection.abandon(messageID); 1241 } 1242 1243 throw le; 1244 } 1245 1246 if (response instanceof IntermediateResponse) 1247 { 1248 final IntermediateResponseListener listener = 1249 getIntermediateResponseListener(); 1250 if (listener != null) 1251 { 1252 listener.intermediateResponseReturned( 1253 (IntermediateResponse) response); 1254 } 1255 } 1256 else 1257 { 1258 return handleResponse(connection, response, requestTime, depth); 1259 } 1260 } 1261 } 1262 1263 1264 1265 /** 1266 * Performs the necessary processing for handling a response. 1267 * 1268 * @param connection The connection used to read the response. 1269 * @param response The response to be processed. 1270 * @param requestTime The time the request was sent to the server. 1271 * @param depth The current referral depth for this request. It 1272 * should always be one for the initial request, and 1273 * should only be incremented when following referrals. 1274 * 1275 * @return The add result. 1276 * 1277 * @throws LDAPException If a problem occurs. 1278 */ 1279 @NotNull() 1280 private LDAPResult handleResponse(@NotNull final LDAPConnection connection, 1281 @Nullable final LDAPResponse response, 1282 final long requestTime, final int depth) 1283 throws LDAPException 1284 { 1285 if (response == null) 1286 { 1287 final long waitTime = 1288 StaticUtils.nanosToMillis(System.nanoTime() - requestTime); 1289 if (connection.getConnectionOptions().abandonOnTimeout()) 1290 { 1291 connection.abandon(messageID); 1292 } 1293 1294 throw new LDAPException(ResultCode.TIMEOUT, 1295 ERR_ADD_CLIENT_TIMEOUT.get(waitTime, messageID, dn, 1296 connection.getHostPort())); 1297 } 1298 1299 connection.getConnectionStatistics().incrementNumAddResponses( 1300 System.nanoTime() - requestTime); 1301 1302 if (response instanceof ConnectionClosedResponse) 1303 { 1304 // The connection was closed while waiting for the response. 1305 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response; 1306 final String message = ccr.getMessage(); 1307 if (message == null) 1308 { 1309 throw new LDAPException(ccr.getResultCode(), 1310 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE.get( 1311 connection.getHostPort(), toString())); 1312 } 1313 else 1314 { 1315 throw new LDAPException(ccr.getResultCode(), 1316 ERR_CONN_CLOSED_WAITING_FOR_ADD_RESPONSE_WITH_MESSAGE.get( 1317 connection.getHostPort(), toString(), message)); 1318 } 1319 } 1320 1321 final LDAPResult result = (LDAPResult) response; 1322 if ((result.getResultCode().equals(ResultCode.REFERRAL)) && 1323 followReferrals(connection)) 1324 { 1325 if (depth >= connection.getConnectionOptions().getReferralHopLimit()) 1326 { 1327 return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED, 1328 ERR_TOO_MANY_REFERRALS.get(), 1329 result.getMatchedDN(), 1330 result.getReferralURLs(), 1331 result.getResponseControls()); 1332 } 1333 1334 return ReferralHelper.handleReferral(this, result, connection); 1335 } 1336 else 1337 { 1338 return result; 1339 } 1340 } 1341 1342 1343 1344 /** 1345 * {@inheritDoc} 1346 */ 1347 @Override() 1348 public int getLastMessageID() 1349 { 1350 return messageID; 1351 } 1352 1353 1354 1355 /** 1356 * {@inheritDoc} 1357 */ 1358 @Override() 1359 @NotNull() 1360 public OperationType getOperationType() 1361 { 1362 return OperationType.ADD; 1363 } 1364 1365 1366 1367 /** 1368 * {@inheritDoc} 1369 */ 1370 @Override() 1371 @NotNull() 1372 public AddRequest duplicate() 1373 { 1374 return duplicate(getControls()); 1375 } 1376 1377 1378 1379 /** 1380 * {@inheritDoc} 1381 */ 1382 @Override() 1383 @NotNull() 1384 public AddRequest duplicate(@Nullable final Control[] controls) 1385 { 1386 final ArrayList<Attribute> attrs = new ArrayList<>(attributes); 1387 final AddRequest r = new AddRequest(dn, attrs, controls); 1388 1389 if (followReferralsInternal() != null) 1390 { 1391 r.setFollowReferrals(followReferralsInternal()); 1392 } 1393 1394 if (getReferralConnectorInternal() != null) 1395 { 1396 r.setReferralConnector(getReferralConnectorInternal()); 1397 } 1398 1399 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 1400 r.setIntermediateResponseListener(getIntermediateResponseListener()); 1401 r.setReferralDepth(getReferralDepth()); 1402 r.setReferralConnector(getReferralConnectorInternal()); 1403 1404 return r; 1405 } 1406 1407 1408 1409 /** 1410 * {@inheritDoc} 1411 */ 1412 @InternalUseOnly() 1413 @Override() 1414 public void responseReceived(@NotNull final LDAPResponse response) 1415 throws LDAPException 1416 { 1417 try 1418 { 1419 responseQueue.put(response); 1420 } 1421 catch (final Exception e) 1422 { 1423 Debug.debugException(e); 1424 1425 if (e instanceof InterruptedException) 1426 { 1427 Thread.currentThread().interrupt(); 1428 } 1429 1430 throw new LDAPException(ResultCode.LOCAL_ERROR, 1431 ERR_EXCEPTION_HANDLING_RESPONSE.get( 1432 StaticUtils.getExceptionMessage(e)), 1433 e); 1434 } 1435 } 1436 1437 1438 1439 /** 1440 * {@inheritDoc} 1441 */ 1442 @Override() 1443 @NotNull() 1444 public LDIFAddChangeRecord toLDIFChangeRecord() 1445 { 1446 return new LDIFAddChangeRecord(this); 1447 } 1448 1449 1450 1451 /** 1452 * {@inheritDoc} 1453 */ 1454 @Override() 1455 @NotNull() 1456 public String[] toLDIF() 1457 { 1458 return toLDIFChangeRecord().toLDIF(); 1459 } 1460 1461 1462 1463 /** 1464 * {@inheritDoc} 1465 */ 1466 @Override() 1467 @NotNull() 1468 public String toLDIFString() 1469 { 1470 return toLDIFChangeRecord().toLDIFString(); 1471 } 1472 1473 1474 1475 /** 1476 * {@inheritDoc} 1477 */ 1478 @Override() 1479 public void toString(@NotNull final StringBuilder buffer) 1480 { 1481 buffer.append("AddRequest(dn='"); 1482 buffer.append(dn); 1483 buffer.append("', attrs={"); 1484 1485 for (int i=0; i < attributes.size(); i++) 1486 { 1487 if (i > 0) 1488 { 1489 buffer.append(", "); 1490 } 1491 1492 buffer.append(attributes.get(i)); 1493 } 1494 buffer.append('}'); 1495 1496 final Control[] controls = getControls(); 1497 if (controls.length > 0) 1498 { 1499 buffer.append(", controls={"); 1500 for (int i=0; i < controls.length; i++) 1501 { 1502 if (i > 0) 1503 { 1504 buffer.append(", "); 1505 } 1506 1507 buffer.append(controls[i]); 1508 } 1509 buffer.append('}'); 1510 } 1511 1512 buffer.append(')'); 1513 } 1514 1515 1516 1517 /** 1518 * {@inheritDoc} 1519 */ 1520 @Override() 1521 public void toCode(@NotNull final List<String> lineList, 1522 @NotNull final String requestID, 1523 final int indentSpaces, final boolean includeProcessing) 1524 { 1525 // Create the request variable. 1526 final ArrayList<ToCodeArgHelper> constructorArgs = 1527 new ArrayList<>(attributes.size() + 1); 1528 constructorArgs.add(ToCodeArgHelper.createString(dn, "Entry DN")); 1529 1530 boolean firstAttribute = true; 1531 for (final Attribute a : attributes) 1532 { 1533 final String comment; 1534 if (firstAttribute) 1535 { 1536 firstAttribute = false; 1537 comment = "Entry Attributes"; 1538 } 1539 else 1540 { 1541 comment = null; 1542 } 1543 1544 constructorArgs.add(ToCodeArgHelper.createAttribute(a, comment)); 1545 } 1546 1547 ToCodeHelper.generateMethodCall(lineList, indentSpaces, "AddRequest", 1548 requestID + "Request", "new AddRequest", constructorArgs); 1549 1550 1551 // If there are any controls, then add them to the request. 1552 for (final Control c : getControls()) 1553 { 1554 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 1555 requestID + "Request.addControl", 1556 ToCodeArgHelper.createControl(c, null)); 1557 } 1558 1559 1560 // Add lines for processing the request and obtaining the result. 1561 if (includeProcessing) 1562 { 1563 // Generate a string with the appropriate indent. 1564 final StringBuilder buffer = new StringBuilder(); 1565 for (int i=0; i < indentSpaces; i++) 1566 { 1567 buffer.append(' '); 1568 } 1569 final String indent = buffer.toString(); 1570 1571 lineList.add(""); 1572 lineList.add(indent + "try"); 1573 lineList.add(indent + '{'); 1574 lineList.add(indent + " LDAPResult " + requestID + 1575 "Result = connection.add(" + requestID + "Request);"); 1576 lineList.add(indent + " // The add was processed successfully."); 1577 lineList.add(indent + '}'); 1578 lineList.add(indent + "catch (LDAPException e)"); 1579 lineList.add(indent + '{'); 1580 lineList.add(indent + " // The add failed. Maybe the following will " + 1581 "help explain why."); 1582 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 1583 lineList.add(indent + " String message = e.getMessage();"); 1584 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 1585 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 1586 lineList.add(indent + " Control[] responseControls = " + 1587 "e.getResponseControls();"); 1588 lineList.add(indent + '}'); 1589 } 1590 } 1591}