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