001/* 002 * Copyright 2007-2017 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.util.List; 026import java.util.Timer; 027import java.util.concurrent.LinkedBlockingQueue; 028import java.util.concurrent.TimeUnit; 029 030import com.unboundid.asn1.ASN1Buffer; 031import com.unboundid.asn1.ASN1Element; 032import com.unboundid.asn1.ASN1OctetString; 033import com.unboundid.ldap.protocol.LDAPMessage; 034import com.unboundid.ldap.protocol.LDAPResponse; 035import com.unboundid.ldap.protocol.ProtocolOp; 036import com.unboundid.ldif.LDIFDeleteChangeRecord; 037import com.unboundid.util.InternalUseOnly; 038import com.unboundid.util.Mutable; 039import com.unboundid.util.ThreadSafety; 040import com.unboundid.util.ThreadSafetyLevel; 041 042import static com.unboundid.ldap.sdk.LDAPMessages.*; 043import static com.unboundid.util.Debug.*; 044import static com.unboundid.util.StaticUtils.*; 045import static com.unboundid.util.Validator.*; 046 047 048 049/** 050 * This class implements the processing necessary to perform an LDAPv3 delete 051 * operation, which removes an entry from the directory. A delete request 052 * contains the DN of the entry to remove. It may also include a set of 053 * controls to send to the server. 054 * {@code DeleteRequest} objects are mutable and therefore can be altered and 055 * re-used for multiple requests. Note, however, that {@code DeleteRequest} 056 * objects are not threadsafe and therefore a single {@code DeleteRequest} 057 * object instance should not be used to process multiple requests at the same 058 * time. 059 * <BR><BR> 060 * <H2>Example</H2> 061 * The following example demonstrates the process for performing a delete 062 * operation: 063 * <PRE> 064 * DeleteRequest deleteRequest = 065 * new DeleteRequest("cn=entry to delete,dc=example,dc=com"); 066 * LDAPResult deleteResult; 067 * try 068 * { 069 * deleteResult = connection.delete(deleteRequest); 070 * // If we get here, the delete was successful. 071 * } 072 * catch (LDAPException le) 073 * { 074 * // The delete operation failed. 075 * deleteResult = le.toLDAPResult(); 076 * ResultCode resultCode = le.getResultCode(); 077 * String errorMessageFromServer = le.getDiagnosticMessage(); 078 * } 079 * </PRE> 080 */ 081@Mutable() 082@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 083public final class DeleteRequest 084 extends UpdatableLDAPRequest 085 implements ReadOnlyDeleteRequest, ResponseAcceptor, ProtocolOp 086{ 087 /** 088 * The serial version UID for this serializable class. 089 */ 090 private static final long serialVersionUID = -6126029442850884239L; 091 092 093 094 // The message ID from the last LDAP message sent from this request. 095 private int messageID = -1; 096 097 // The queue that will be used to receive response messages from the server. 098 private final LinkedBlockingQueue<LDAPResponse> responseQueue = 099 new LinkedBlockingQueue<LDAPResponse>(); 100 101 // The DN of the entry to delete. 102 private String dn; 103 104 105 106 /** 107 * Creates a new delete request with the provided DN. 108 * 109 * @param dn The DN of the entry to delete. It must not be {@code null}. 110 */ 111 public DeleteRequest(final String dn) 112 { 113 super(null); 114 115 ensureNotNull(dn); 116 117 this.dn = dn; 118 } 119 120 121 122 /** 123 * Creates a new delete request with the provided DN. 124 * 125 * @param dn The DN of the entry to delete. It must not be 126 * {@code null}. 127 * @param controls The set of controls to include in the request. 128 */ 129 public DeleteRequest(final String dn, final Control[] controls) 130 { 131 super(controls); 132 133 ensureNotNull(dn); 134 135 this.dn = dn; 136 } 137 138 139 140 /** 141 * Creates a new delete request with the provided DN. 142 * 143 * @param dn The DN of the entry to delete. It must not be {@code null}. 144 */ 145 public DeleteRequest(final DN dn) 146 { 147 super(null); 148 149 ensureNotNull(dn); 150 151 this.dn = dn.toString(); 152 } 153 154 155 156 /** 157 * Creates a new delete request with the provided DN. 158 * 159 * @param dn The DN of the entry to delete. It must not be 160 * {@code null}. 161 * @param controls The set of controls to include in the request. 162 */ 163 public DeleteRequest(final DN dn, final Control[] controls) 164 { 165 super(controls); 166 167 ensureNotNull(dn); 168 169 this.dn = dn.toString(); 170 } 171 172 173 174 /** 175 * {@inheritDoc} 176 */ 177 @Override() 178 public String getDN() 179 { 180 return dn; 181 } 182 183 184 185 /** 186 * Specifies the DN of the entry to delete. 187 * 188 * @param dn The DN of the entry to delete. It must not be {@code null}. 189 */ 190 public void setDN(final String dn) 191 { 192 ensureNotNull(dn); 193 194 this.dn = dn; 195 } 196 197 198 199 /** 200 * Specifies the DN of the entry to delete. 201 * 202 * @param dn The DN of the entry to delete. It must not be {@code null}. 203 */ 204 public void setDN(final DN dn) 205 { 206 ensureNotNull(dn); 207 208 this.dn = dn.toString(); 209 } 210 211 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override() 217 public byte getProtocolOpType() 218 { 219 return LDAPMessage.PROTOCOL_OP_TYPE_DELETE_REQUEST; 220 } 221 222 223 224 /** 225 * {@inheritDoc} 226 */ 227 @Override() 228 public void writeTo(final ASN1Buffer buffer) 229 { 230 buffer.addOctetString(LDAPMessage.PROTOCOL_OP_TYPE_DELETE_REQUEST, dn); 231 } 232 233 234 235 /** 236 * Encodes the delete request protocol op to an ASN.1 element. 237 * 238 * @return The ASN.1 element with the encoded delete request protocol op. 239 */ 240 @Override() 241 public ASN1Element encodeProtocolOp() 242 { 243 return new ASN1OctetString(LDAPMessage.PROTOCOL_OP_TYPE_DELETE_REQUEST, dn); 244 } 245 246 247 248 /** 249 * Sends this delete request to the directory server over the provided 250 * connection and returns the associated response. 251 * 252 * @param connection The connection to use to communicate with the directory 253 * server. 254 * @param depth The current referral depth for this request. It should 255 * always be one for the initial request, and should only 256 * be incremented when following referrals. 257 * 258 * @return An LDAP result object that provides information about the result 259 * of the delete processing. 260 * 261 * @throws LDAPException If a problem occurs while sending the request or 262 * reading the response. 263 */ 264 @Override() 265 protected LDAPResult process(final LDAPConnection connection, final int depth) 266 throws LDAPException 267 { 268 if (connection.synchronousMode()) 269 { 270 @SuppressWarnings("deprecation") 271 final boolean autoReconnect = 272 connection.getConnectionOptions().autoReconnect(); 273 return processSync(connection, depth, autoReconnect); 274 } 275 276 final long requestTime = System.nanoTime(); 277 processAsync(connection, null); 278 279 try 280 { 281 // Wait for and process the response. 282 final LDAPResponse response; 283 try 284 { 285 final long responseTimeout = getResponseTimeoutMillis(connection); 286 if (responseTimeout > 0) 287 { 288 response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS); 289 } 290 else 291 { 292 response = responseQueue.take(); 293 } 294 } 295 catch (final InterruptedException ie) 296 { 297 debugException(ie); 298 Thread.currentThread().interrupt(); 299 throw new LDAPException(ResultCode.LOCAL_ERROR, 300 ERR_DELETE_INTERRUPTED.get(connection.getHostPort()), ie); 301 } 302 303 return handleResponse(connection, response, requestTime, depth, false); 304 } 305 finally 306 { 307 connection.deregisterResponseAcceptor(messageID); 308 } 309 } 310 311 312 313 /** 314 * Sends this delete request to the directory server over the provided 315 * connection and returns the message ID for the request. 316 * 317 * @param connection The connection to use to communicate with the 318 * directory server. 319 * @param resultListener The async result listener that is to be notified 320 * when the response is received. It may be 321 * {@code null} only if the result is to be processed 322 * by this class. 323 * 324 * @return The async request ID created for the operation, or {@code null} if 325 * the provided {@code resultListener} is {@code null} and the 326 * operation will not actually be processed asynchronously. 327 * 328 * @throws LDAPException If a problem occurs while sending the request. 329 */ 330 AsyncRequestID processAsync(final LDAPConnection connection, 331 final AsyncResultListener resultListener) 332 throws LDAPException 333 { 334 // Create the LDAP message. 335 messageID = connection.nextMessageID(); 336 final LDAPMessage message = new LDAPMessage(messageID, this, getControls()); 337 338 339 // If the provided async result listener is {@code null}, then we'll use 340 // this class as the message acceptor. Otherwise, create an async helper 341 // and use it as the message acceptor. 342 final AsyncRequestID asyncRequestID; 343 if (resultListener == null) 344 { 345 asyncRequestID = null; 346 connection.registerResponseAcceptor(messageID, this); 347 } 348 else 349 { 350 final AsyncHelper helper = new AsyncHelper(connection, 351 OperationType.DELETE, messageID, resultListener, 352 getIntermediateResponseListener()); 353 connection.registerResponseAcceptor(messageID, helper); 354 asyncRequestID = helper.getAsyncRequestID(); 355 356 final long timeout = getResponseTimeoutMillis(connection); 357 if (timeout > 0L) 358 { 359 final Timer timer = connection.getTimer(); 360 final AsyncTimeoutTimerTask timerTask = 361 new AsyncTimeoutTimerTask(helper); 362 timer.schedule(timerTask, timeout); 363 asyncRequestID.setTimerTask(timerTask); 364 } 365 } 366 367 368 // Send the request to the server. 369 try 370 { 371 debugLDAPRequest(this); 372 connection.getConnectionStatistics().incrementNumDeleteRequests(); 373 connection.sendMessage(message); 374 return asyncRequestID; 375 } 376 catch (final LDAPException le) 377 { 378 debugException(le); 379 380 connection.deregisterResponseAcceptor(messageID); 381 throw le; 382 } 383 } 384 385 386 387 /** 388 * Processes this delete operation in synchronous mode, in which the same 389 * thread will send the request and read the response. 390 * 391 * @param connection The connection to use to communicate with the directory 392 * server. 393 * @param depth The current referral depth for this request. It should 394 * always be one for the initial request, and should only 395 * be incremented when following referrals. 396 * @param allowRetry Indicates whether the request may be re-tried on a 397 * re-established connection if the initial attempt fails 398 * in a way that indicates the connection is no longer 399 * valid and autoReconnect is true. 400 * 401 * @return An LDAP result object that provides information about the result 402 * of the delete processing. 403 * 404 * @throws LDAPException If a problem occurs while sending the request or 405 * reading the response. 406 */ 407 private LDAPResult processSync(final LDAPConnection connection, 408 final int depth, final boolean allowRetry) 409 throws LDAPException 410 { 411 // Create the LDAP message. 412 messageID = connection.nextMessageID(); 413 final LDAPMessage message = 414 new LDAPMessage(messageID, this, getControls()); 415 416 417 // Set the appropriate timeout on the socket. 418 try 419 { 420 connection.getConnectionInternals(true).getSocket().setSoTimeout( 421 (int) getResponseTimeoutMillis(connection)); 422 } 423 catch (final Exception e) 424 { 425 debugException(e); 426 } 427 428 429 // Send the request to the server. 430 final long requestTime = System.nanoTime(); 431 debugLDAPRequest(this); 432 connection.getConnectionStatistics().incrementNumDeleteRequests(); 433 try 434 { 435 connection.sendMessage(message); 436 } 437 catch (final LDAPException le) 438 { 439 debugException(le); 440 441 if (allowRetry) 442 { 443 final LDAPResult retryResult = reconnectAndRetry(connection, depth, 444 le.getResultCode()); 445 if (retryResult != null) 446 { 447 return retryResult; 448 } 449 } 450 451 throw le; 452 } 453 454 while (true) 455 { 456 final LDAPResponse response; 457 try 458 { 459 response = connection.readResponse(messageID); 460 } 461 catch (final LDAPException le) 462 { 463 debugException(le); 464 465 if ((le.getResultCode() == ResultCode.TIMEOUT) && 466 connection.getConnectionOptions().abandonOnTimeout()) 467 { 468 connection.abandon(messageID); 469 } 470 471 if (allowRetry) 472 { 473 final LDAPResult retryResult = reconnectAndRetry(connection, depth, 474 le.getResultCode()); 475 if (retryResult != null) 476 { 477 return retryResult; 478 } 479 } 480 481 throw le; 482 } 483 484 if (response instanceof IntermediateResponse) 485 { 486 final IntermediateResponseListener listener = 487 getIntermediateResponseListener(); 488 if (listener != null) 489 { 490 listener.intermediateResponseReturned( 491 (IntermediateResponse) response); 492 } 493 } 494 else 495 { 496 return handleResponse(connection, response, requestTime, depth, 497 allowRetry); 498 } 499 } 500 } 501 502 503 504 /** 505 * Performs the necessary processing for handling a response. 506 * 507 * @param connection The connection used to read the response. 508 * @param response The response to be processed. 509 * @param requestTime The time the request was sent to the server. 510 * @param depth The current referral depth for this request. It 511 * should always be one for the initial request, and 512 * should only be incremented when following referrals. 513 * @param allowRetry Indicates whether the request may be re-tried on a 514 * re-established connection if the initial attempt fails 515 * in a way that indicates the connection is no longer 516 * valid and autoReconnect is true. 517 * 518 * @return The delete result. 519 * 520 * @throws LDAPException If a problem occurs. 521 */ 522 private LDAPResult handleResponse(final LDAPConnection connection, 523 final LDAPResponse response, 524 final long requestTime, final int depth, 525 final boolean allowRetry) 526 throws LDAPException 527 { 528 if (response == null) 529 { 530 final long waitTime = nanosToMillis(System.nanoTime() - requestTime); 531 if (connection.getConnectionOptions().abandonOnTimeout()) 532 { 533 connection.abandon(messageID); 534 } 535 536 throw new LDAPException(ResultCode.TIMEOUT, 537 ERR_DELETE_CLIENT_TIMEOUT.get(waitTime, messageID, dn, 538 connection.getHostPort())); 539 } 540 541 connection.getConnectionStatistics().incrementNumDeleteResponses( 542 System.nanoTime() - requestTime); 543 if (response instanceof ConnectionClosedResponse) 544 { 545 // The connection was closed while waiting for the response. 546 if (allowRetry) 547 { 548 final LDAPResult retryResult = reconnectAndRetry(connection, depth, 549 ResultCode.SERVER_DOWN); 550 if (retryResult != null) 551 { 552 return retryResult; 553 } 554 } 555 556 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response; 557 final String message = ccr.getMessage(); 558 if (message == null) 559 { 560 throw new LDAPException(ccr.getResultCode(), 561 ERR_CONN_CLOSED_WAITING_FOR_DELETE_RESPONSE.get( 562 connection.getHostPort(), toString())); 563 } 564 else 565 { 566 throw new LDAPException(ccr.getResultCode(), 567 ERR_CONN_CLOSED_WAITING_FOR_DELETE_RESPONSE_WITH_MESSAGE.get( 568 connection.getHostPort(), toString(), message)); 569 } 570 } 571 572 final LDAPResult result = (LDAPResult) response; 573 if ((result.getResultCode().equals(ResultCode.REFERRAL)) && 574 followReferrals(connection)) 575 { 576 if (depth >= connection.getConnectionOptions().getReferralHopLimit()) 577 { 578 return new LDAPResult(messageID, ResultCode.REFERRAL_LIMIT_EXCEEDED, 579 ERR_TOO_MANY_REFERRALS.get(), 580 result.getMatchedDN(), result.getReferralURLs(), 581 result.getResponseControls()); 582 } 583 584 return followReferral(result, connection, depth); 585 } 586 else 587 { 588 if (allowRetry) 589 { 590 final LDAPResult retryResult = reconnectAndRetry(connection, depth, 591 result.getResultCode()); 592 if (retryResult != null) 593 { 594 return retryResult; 595 } 596 } 597 598 return result; 599 } 600 } 601 602 603 604 /** 605 * Attempts to re-establish the connection and retry processing this request 606 * on it. 607 * 608 * @param connection The connection to be re-established. 609 * @param depth The current referral depth for this request. It should 610 * always be one for the initial request, and should only 611 * be incremented when following referrals. 612 * @param resultCode The result code for the previous operation attempt. 613 * 614 * @return The result from re-trying the add, or {@code null} if it could not 615 * be re-tried. 616 */ 617 private LDAPResult reconnectAndRetry(final LDAPConnection connection, 618 final int depth, 619 final ResultCode resultCode) 620 { 621 try 622 { 623 // We will only want to retry for certain result codes that indicate a 624 // connection problem. 625 switch (resultCode.intValue()) 626 { 627 case ResultCode.SERVER_DOWN_INT_VALUE: 628 case ResultCode.DECODING_ERROR_INT_VALUE: 629 case ResultCode.CONNECT_ERROR_INT_VALUE: 630 connection.reconnect(); 631 return processSync(connection, depth, false); 632 } 633 } 634 catch (final Exception e) 635 { 636 debugException(e); 637 } 638 639 return null; 640 } 641 642 643 644 /** 645 * Attempts to follow a referral to perform a delete operation in the target 646 * server. 647 * 648 * @param referralResult The LDAP result object containing information about 649 * the referral to follow. 650 * @param connection The connection on which the referral was received. 651 * @param depth The number of referrals followed in the course of 652 * processing this request. 653 * 654 * @return The result of attempting to process the delete operation by 655 * following the referral. 656 * 657 * @throws LDAPException If a problem occurs while attempting to establish 658 * the referral connection, sending the request, or 659 * reading the result. 660 */ 661 private LDAPResult followReferral(final LDAPResult referralResult, 662 final LDAPConnection connection, 663 final int depth) 664 throws LDAPException 665 { 666 for (final String urlString : referralResult.getReferralURLs()) 667 { 668 try 669 { 670 final LDAPURL referralURL = new LDAPURL(urlString); 671 final String host = referralURL.getHost(); 672 673 if (host == null) 674 { 675 // We can't handle a referral in which there is no host. 676 continue; 677 } 678 679 final DeleteRequest deleteRequest; 680 if (referralURL.baseDNProvided()) 681 { 682 deleteRequest = new DeleteRequest(referralURL.getBaseDN(), 683 getControls()); 684 } 685 else 686 { 687 deleteRequest = this; 688 } 689 690 final LDAPConnection referralConn = connection.getReferralConnector(). 691 getReferralConnection(referralURL, connection); 692 try 693 { 694 return deleteRequest.process(referralConn, depth+1); 695 } 696 finally 697 { 698 referralConn.setDisconnectInfo(DisconnectType.REFERRAL, null, null); 699 referralConn.close(); 700 } 701 } 702 catch (final LDAPException le) 703 { 704 debugException(le); 705 } 706 } 707 708 // If we've gotten here, then we could not follow any of the referral URLs, 709 // so we'll just return the original referral result. 710 return referralResult; 711 } 712 713 714 715 /** 716 * {@inheritDoc} 717 */ 718 @InternalUseOnly() 719 @Override() 720 public void responseReceived(final LDAPResponse response) 721 throws LDAPException 722 { 723 try 724 { 725 responseQueue.put(response); 726 } 727 catch (final Exception e) 728 { 729 debugException(e); 730 731 if (e instanceof InterruptedException) 732 { 733 Thread.currentThread().interrupt(); 734 } 735 736 throw new LDAPException(ResultCode.LOCAL_ERROR, 737 ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e); 738 } 739 } 740 741 742 743 /** 744 * {@inheritDoc} 745 */ 746 @Override() 747 public int getLastMessageID() 748 { 749 return messageID; 750 } 751 752 753 754 /** 755 * {@inheritDoc} 756 */ 757 @Override() 758 public OperationType getOperationType() 759 { 760 return OperationType.DELETE; 761 } 762 763 764 765 /** 766 * {@inheritDoc} 767 */ 768 @Override() 769 public DeleteRequest duplicate() 770 { 771 return duplicate(getControls()); 772 } 773 774 775 776 /** 777 * {@inheritDoc} 778 */ 779 @Override() 780 public DeleteRequest duplicate(final Control[] controls) 781 { 782 final DeleteRequest r = new DeleteRequest(dn, controls); 783 784 if (followReferralsInternal() != null) 785 { 786 r.setFollowReferrals(followReferralsInternal()); 787 } 788 789 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 790 791 return r; 792 } 793 794 795 796 /** 797 * {@inheritDoc} 798 */ 799 @Override() 800 public LDIFDeleteChangeRecord toLDIFChangeRecord() 801 { 802 return new LDIFDeleteChangeRecord(this); 803 } 804 805 806 807 /** 808 * {@inheritDoc} 809 */ 810 @Override() 811 public String[] toLDIF() 812 { 813 return toLDIFChangeRecord().toLDIF(); 814 } 815 816 817 818 /** 819 * {@inheritDoc} 820 */ 821 @Override() 822 public String toLDIFString() 823 { 824 return toLDIFChangeRecord().toLDIFString(); 825 } 826 827 828 829 /** 830 * {@inheritDoc} 831 */ 832 @Override() 833 public void toString(final StringBuilder buffer) 834 { 835 buffer.append("DeleteRequest(dn='"); 836 buffer.append(dn); 837 buffer.append('\''); 838 839 final Control[] controls = getControls(); 840 if (controls.length > 0) 841 { 842 buffer.append(", controls={"); 843 for (int i=0; i < controls.length; i++) 844 { 845 if (i > 0) 846 { 847 buffer.append(", "); 848 } 849 850 buffer.append(controls[i]); 851 } 852 buffer.append('}'); 853 } 854 855 buffer.append(')'); 856 } 857 858 859 860 /** 861 * {@inheritDoc} 862 */ 863 @Override() 864 public void toCode(final List<String> lineList, final String requestID, 865 final int indentSpaces, final boolean includeProcessing) 866 { 867 // Create the request variable. 868 ToCodeHelper.generateMethodCall(lineList, indentSpaces, "DeleteRequest", 869 requestID + "Request", "new DeleteRequest", 870 ToCodeArgHelper.createString(dn, "Entry DN")); 871 872 // If there are any controls, then add them to the request. 873 for (final Control c : getControls()) 874 { 875 ToCodeHelper.generateMethodCall(lineList, indentSpaces, null, null, 876 requestID + "Request.addControl", 877 ToCodeArgHelper.createControl(c, null)); 878 } 879 880 881 // Add lines for processing the request and obtaining the result. 882 if (includeProcessing) 883 { 884 // Generate a string with the appropriate indent. 885 final StringBuilder buffer = new StringBuilder(); 886 for (int i=0; i < indentSpaces; i++) 887 { 888 buffer.append(' '); 889 } 890 final String indent = buffer.toString(); 891 892 lineList.add(""); 893 lineList.add(indent + "try"); 894 lineList.add(indent + '{'); 895 lineList.add(indent + " LDAPResult " + requestID + 896 "Result = connection.delete(" + requestID + "Request);"); 897 lineList.add(indent + " // The delete was processed successfully."); 898 lineList.add(indent + '}'); 899 lineList.add(indent + "catch (LDAPException e)"); 900 lineList.add(indent + '{'); 901 lineList.add(indent + " // The delete failed. Maybe the following " + 902 "will help explain why."); 903 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 904 lineList.add(indent + " String message = e.getMessage();"); 905 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 906 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 907 lineList.add(indent + " Control[] responseControls = " + 908 "e.getResponseControls();"); 909 lineList.add(indent + '}'); 910 } 911 } 912}