001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2009 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.protocols.internal; 028 029import java.io.IOException; 030import java.io.InputStream; 031import java.io.OutputStream; 032import java.util.List; 033 034import org.forgerock.i18n.LocalizableMessage; 035import org.forgerock.opendj.io.ASN1; 036import org.forgerock.opendj.io.ASN1Reader; 037import org.forgerock.opendj.ldap.ByteSequenceReader; 038import org.forgerock.opendj.ldap.ByteString; 039import org.forgerock.opendj.ldap.ByteStringBuilder; 040import org.opends.server.core.*; 041import org.opends.server.protocols.ldap.*; 042import org.opends.server.types.*; 043 044import static org.forgerock.opendj.ldap.DecodeException.*; 045import static org.opends.messages.ProtocolMessages.*; 046import static org.opends.server.protocols.internal.InternalClientConnection.*; 047import static org.opends.server.protocols.internal.Requests.*; 048import static org.opends.server.protocols.ldap.LDAPConstants.*; 049import static org.opends.server.util.ServerConstants.*; 050 051/** 052 * This class provides an implementation of a 053 * {@code java.io.OutputStream} that can be used to facilitate 054 * internal communication with the Directory Server. On the backend, 055 * data written to this output stream will be first decoded as an 056 * ASN.1 element and then as an LDAP message. That LDAP message will 057 * be converted to an internal operation which will then be processed 058 * and the result returned to the client via the input stream on the 059 * other side of the associated internal LDAP socket. 060 */ 061@org.opends.server.types.PublicAPI( 062 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 063 mayInstantiate=false, 064 mayExtend=false, 065 mayInvoke=true) 066public final class InternalLDAPOutputStream 067 extends OutputStream 068 implements InternalSearchListener 069{ 070 /** Indicates whether this stream has been closed. */ 071 private boolean closed; 072 073 private final ASN1Reader reader; 074 075 /** The internal LDAP socket with which this output stream is associated. */ 076 private final InternalLDAPSocket socket; 077 078 /** The immediate data being written. */ 079 private ByteSequenceReader byteBuffer; 080 081 /** 082 * The save buffer used to store any unprocessed data waiting 083 * to be read as ASN.1 elements. (Usually due to writing incomplete 084 * ASN.1 elements.) 085 */ 086 private final ByteStringBuilder saveBuffer; 087 088 private final ByteSequenceReader saveBufferReader; 089 090 /** 091 * An adaptor class for reading from a save buffer and the bytes 092 * being written sequentially using the InputStream interface. 093 * 094 * Since the bytes being written are only available duing the write 095 * call, any unused data will be appended to the save buffer before 096 * returning from the write method. This reader will always read the 097 * save buffer first before the actual bytes being written to ensure 098 * bytes are read in the same order as they are written. 099 */ 100 private class CombinedBufferInputStream extends InputStream 101 { 102 /** {@inheritDoc} */ 103 @Override 104 public int available() 105 { 106 // The number of available bytes is the sum of the save buffer 107 // and the last read data in the NIO ByteStringBuilder. 108 return saveBufferReader.remaining() + byteBuffer.remaining(); 109 } 110 111 /** {@inheritDoc} */ 112 @Override 113 public int read() 114 { 115 if(saveBufferReader.remaining() > 0) 116 { 117 // Try saved buffer first 118 return 0xFF & saveBufferReader.readByte(); 119 } 120 if(byteBuffer.remaining() > 0) 121 { 122 // Must still be on the channel buffer 123 return 0xFF & byteBuffer.readByte(); 124 } 125 126 return -1; 127 } 128 129 /** {@inheritDoc} */ 130 @Override 131 public int read(byte[] bytes) 132 { 133 return read(bytes, 0, bytes.length); 134 } 135 136 /** {@inheritDoc} */ 137 @Override 138 public int read(byte[] value, int off, int length) 139 { 140 int bytesCopied=0; 141 int len; 142 if(saveBufferReader.remaining() > 0) 143 { 144 // Copy out of the last saved buffer first 145 len = Math.min(saveBufferReader.remaining(), length); 146 saveBufferReader.readBytes(value, off, len); 147 bytesCopied += len; 148 } 149 if(bytesCopied < length && byteBuffer.remaining() > 0) 150 { 151 // Copy out of the channel buffer if we haven't got 152 // everything we needed. 153 len = Math.min(byteBuffer.remaining(), length - bytesCopied); 154 byteBuffer.readBytes(value, off + bytesCopied, len); 155 bytesCopied += len; 156 } 157 return bytesCopied; 158 } 159 160 /** {@inheritDoc} */ 161 @Override 162 public long skip(long length) 163 { 164 int bytesSkipped=0; 165 int len; 166 if(saveBufferReader.remaining() > 0) 167 { 168 // Skip in the last saved buffer first 169 len = Math.min(saveBufferReader.remaining(), (int)length); 170 saveBufferReader.position(saveBufferReader.position() + len); 171 bytesSkipped += len; 172 } 173 if(bytesSkipped < length && byteBuffer.remaining() > 0) 174 { 175 //Skip in the channel buffer if we haven't skipped enough. 176 len = Math.min(byteBuffer.remaining(), 177 (int)length - bytesSkipped); 178 byteBuffer.position(byteBuffer.position() + len); 179 bytesSkipped += len; 180 } 181 return bytesSkipped; 182 } 183 } 184 185 /** 186 * Creates a new instance of an internal LDAP output stream that is 187 * associated with the provided internal LDAP socket. 188 * 189 * @param socket The internal LDAP socket that will be serviced by 190 * this internal LDAP output stream. 191 */ 192 public InternalLDAPOutputStream(InternalLDAPSocket socket) 193 { 194 this.socket = socket; 195 this.closed = false; 196 this.saveBuffer = new ByteStringBuilder(); 197 this.saveBufferReader = saveBuffer.asReader(); 198 199 CombinedBufferInputStream bufferStream = 200 new CombinedBufferInputStream(); 201 this.reader = ASN1.getReader(bufferStream); 202 } 203 204 205 206 /** 207 * Closes this output stream, its associated socket, and the 208 * socket's associated input stream. 209 */ 210 @Override 211 public void close() 212 { 213 socket.close(); 214 } 215 216 217 218 /** 219 * Closes this output stream through an internal mechanism that will 220 * not cause an infinite recursion loop by trying to also close the 221 * output stream. 222 */ 223 @org.opends.server.types.PublicAPI( 224 stability=org.opends.server.types.StabilityLevel.PRIVATE, 225 mayInstantiate=false, 226 mayExtend=false, 227 mayInvoke=false) 228 void closeInternal() 229 { 230 closed = true; 231 } 232 233 234 235 /** 236 * Flushes this output stream and forces any buffered data to be 237 * written out. This will have no effect, since this output 238 * stream implementation does not use buffering. 239 */ 240 @Override 241 public void flush() 242 { 243 // No implementation is required. 244 } 245 246 247 248 /** 249 * Writes the contents of the provided byte array to this output 250 * stream. 251 * 252 * @param b The byte array to be written. 253 * 254 * @throws IOException If the output stream is closed, or if there 255 * is a problem with the data being written. 256 */ 257 @Override 258 public void write(byte[] b) 259 throws IOException 260 { 261 write(b, 0, b.length); 262 } 263 264 265 266 /** 267 * Writes the specified portion of the data in the provided byte 268 * array to this output stream. Any data written will be 269 * accumulated until a complete ASN.1 element is available, at which 270 * point it will be decoded as an LDAP message and converted to an 271 * internal operation that will be processed. 272 * 273 * @param b The byte array containing the data to be read. 274 * @param off The position in the array at which to start reading 275 * data. 276 * @param len The number of bytes to read from the array. 277 * 278 * @throws IOException If the output stream is closed, or if there 279 * is a problem with the data being written. 280 */ 281 @Override 282 public synchronized void write(byte[] b, int off, int len) 283 throws IOException 284 { 285 if (closed) 286 { 287 LocalizableMessage m = ERR_INTERNALOS_CLOSED.get(); 288 throw new IOException(m.toString()); 289 } 290 291 byteBuffer = ByteString.wrap(b, off, len).asReader(); 292 293 try 294 { 295 while(reader.elementAvailable()) 296 { 297 LDAPMessage msg = LDAPReader.readMessage(reader); 298 processMessage(msg); 299 } 300 } 301 catch(Exception e) 302 { 303 throw new IOException(e.getMessage()); 304 } 305 306 // Clear the save buffer if we have read all of it 307 if(saveBufferReader.remaining() == 0) 308 { 309 saveBuffer.clear(); 310 saveBufferReader.rewind(); 311 } 312 313 // Append any unused data in the channel buffer to the save buffer 314 if(byteBuffer.remaining() > 0) 315 { 316 saveBuffer.appendBytes(byteBuffer, byteBuffer.remaining()); 317 } 318 } 319 320 321 322 /** 323 * Writes a single byte of data to this output stream. If the byte 324 * written completes an ASN.1 element that was in progress, then it 325 * will be decoded as an LDAP message and converted to an internal 326 * operation that will be processed. Otherwise, the data will be 327 * accumulated until a complete element can be formed. 328 * 329 * @param b The byte to be written. 330 * 331 * @throws IOException If the output stream is closed, or if there 332 * is a problem with the data being written. 333 */ 334 @Override 335 public synchronized void write(int b) 336 throws IOException 337 { 338 write(new byte[]{(byte)b}, 0, 1); 339 } 340 341 342 343 /** 344 * Processes the provided ASN.1 element by decoding it as an LDAP 345 * message, converting that to an internal operation, and sending 346 * the appropriate response message(s) to the client through the 347 * corresponding internal LDAP input stream. 348 * 349 * @param message The LDAP message to process. 350 * 351 * @throws IOException If a problem occurs while attempting to 352 * decode the provided ASN.1 element as an 353 * LDAP message. 354 */ 355 private void processMessage(LDAPMessage message) 356 throws IOException 357 { 358 switch (message.getProtocolOpType()) 359 { 360 case OP_TYPE_ABANDON_REQUEST: 361 // No action is required. 362 return; 363 364 case OP_TYPE_ADD_REQUEST: 365 processAddOperation(message); 366 break; 367 368 case OP_TYPE_BIND_REQUEST: 369 processBindOperation(message); 370 break; 371 372 case OP_TYPE_COMPARE_REQUEST: 373 processCompareOperation(message); 374 break; 375 376 377 case OP_TYPE_DELETE_REQUEST: 378 processDeleteOperation(message); 379 break; 380 381 382 case OP_TYPE_EXTENDED_REQUEST: 383 processExtendedOperation(message); 384 break; 385 386 387 case OP_TYPE_MODIFY_REQUEST: 388 processModifyOperation(message); 389 break; 390 391 392 case OP_TYPE_MODIFY_DN_REQUEST: 393 processModifyDNOperation(message); 394 break; 395 396 397 case OP_TYPE_SEARCH_REQUEST: 398 processSearchOperation(message); 399 break; 400 401 402 case OP_TYPE_UNBIND_REQUEST: 403 socket.close(); 404 break; 405 406 407 default: 408 LocalizableMessage m = ERR_INTERNALOS_INVALID_REQUEST.get( 409 message.getProtocolElementName()); 410 throw new IOException(m.toString()); 411 } 412 } 413 414 415 416 /** 417 * Processes the content of the provided LDAP message as an add 418 * operation and returns the appropriate result to the client. 419 * 420 * @param message The LDAP message containing the request to 421 * process. 422 * 423 * @throws IOException If a problem occurs while attempting to 424 * process the operation. 425 */ 426 private void processAddOperation(LDAPMessage message) 427 throws IOException 428 { 429 int messageID = message.getMessageID(); 430 AddRequestProtocolOp request = message.getAddRequestProtocolOp(); 431 432 InternalClientConnection conn = socket.getConnection(); 433 AddOperationBasis op = 434 new AddOperationBasis(conn, InternalClientConnection.nextOperationID(), 435 messageID, message.getControls(), 436 request.getDN(), 437 request.getAttributes()); 438 op.run(); 439 440 AddResponseProtocolOp addResponse = 441 new AddResponseProtocolOp(op.getResultCode().intValue(), 442 op.getErrorMessage().toMessage(), 443 op.getMatchedDN(), 444 op.getReferralURLs()); 445 List<Control> responseControls = op.getResponseControls(); 446 447 socket.getInputStream().addLDAPMessage( 448 new LDAPMessage(messageID, addResponse, responseControls)); 449 } 450 451 452 453 /** 454 * Processes the content of the provided LDAP message as a bind 455 * operation and returns the appropriate result to the client. 456 * 457 * @param message The LDAP message containing the request to 458 * process. 459 * 460 * @throws IOException If a problem occurs while attempting to 461 * process the operation. 462 */ 463 private void processBindOperation(LDAPMessage message) 464 throws IOException 465 { 466 int messageID = message.getMessageID(); 467 BindRequestProtocolOp request = 468 message.getBindRequestProtocolOp(); 469 470 if (request.getAuthenticationType() == AuthenticationType.SASL) 471 { 472 LocalizableMessage m = ERR_INTERNALOS_SASL_BIND_NOT_SUPPORTED.get(); 473 BindResponseProtocolOp bindResponse = 474 new BindResponseProtocolOp( 475 LDAPResultCode.UNWILLING_TO_PERFORM, m); 476 socket.getInputStream().addLDAPMessage( 477 new LDAPMessage(messageID, bindResponse)); 478 return; 479 } 480 481 InternalClientConnection conn = socket.getConnection(); 482 BindOperationBasis op = 483 new BindOperationBasis(conn, nextOperationID(), 484 messageID, message.getControls(), 485 String.valueOf(request.getProtocolVersion()), 486 request.getDN(), request.getSimplePassword()); 487 op.run(); 488 489 BindResponseProtocolOp bindResponse = 490 new BindResponseProtocolOp(op.getResultCode().intValue(), 491 op.getErrorMessage().toMessage(), 492 op.getMatchedDN(), 493 op.getReferralURLs()); 494 List<Control> responseControls = op.getResponseControls(); 495 496 if (bindResponse.getResultCode() == LDAPResultCode.SUCCESS) 497 { 498 socket.setConnection(new InternalClientConnection( 499 op.getAuthenticationInfo())); 500 } 501 502 socket.getInputStream().addLDAPMessage( 503 new LDAPMessage(messageID, bindResponse, responseControls)); 504 } 505 506 507 508 /** 509 * Processes the content of the provided LDAP message as a compare 510 * operation and returns the appropriate result to the client. 511 * 512 * @param message The LDAP message containing the request to 513 * process. 514 * 515 * @throws IOException If a problem occurs while attempting to 516 * process the operation. 517 */ 518 private void processCompareOperation(LDAPMessage message) 519 throws IOException 520 { 521 int messageID = message.getMessageID(); 522 CompareRequestProtocolOp request = 523 message.getCompareRequestProtocolOp(); 524 525 InternalClientConnection conn = socket.getConnection(); 526 CompareOperationBasis op = 527 new CompareOperationBasis(conn, nextOperationID(), 528 messageID, message.getControls(), request.getDN(), 529 request.getAttributeType(), 530 request.getAssertionValue()); 531 op.run(); 532 533 CompareResponseProtocolOp compareResponse = 534 new CompareResponseProtocolOp( 535 op.getResultCode().intValue(), 536 op.getErrorMessage().toMessage(), 537 op.getMatchedDN(), 538 op.getReferralURLs()); 539 List<Control> responseControls = op.getResponseControls(); 540 541 socket.getInputStream().addLDAPMessage( 542 new LDAPMessage(messageID, compareResponse, 543 responseControls)); 544 } 545 546 547 548 /** 549 * Processes the content of the provided LDAP message as a delete 550 * operation and returns the appropriate result to the client. 551 * 552 * @param message The LDAP message containing the request to 553 * process. 554 * 555 * @throws IOException If a problem occurs while attempting to 556 * process the operation. 557 */ 558 private void processDeleteOperation(LDAPMessage message) 559 throws IOException 560 { 561 int messageID = message.getMessageID(); 562 DeleteRequestProtocolOp request = 563 message.getDeleteRequestProtocolOp(); 564 565 InternalClientConnection conn = socket.getConnection(); 566 DeleteOperationBasis op = 567 new DeleteOperationBasis(conn, nextOperationID(), 568 messageID, message.getControls(), request.getDN()); 569 op.run(); 570 571 DeleteResponseProtocolOp deleteResponse = 572 new DeleteResponseProtocolOp( 573 op.getResultCode().intValue(), 574 op.getErrorMessage().toMessage(), 575 op.getMatchedDN(), 576 op.getReferralURLs()); 577 List<Control> responseControls = op.getResponseControls(); 578 579 socket.getInputStream().addLDAPMessage( 580 new LDAPMessage(messageID, deleteResponse, 581 responseControls)); 582 } 583 584 585 586 /** 587 * Processes the content of the provided LDAP message as an extended 588 * operation and returns the appropriate result to the client. 589 * 590 * @param message The LDAP message containing the request to 591 * process. 592 * 593 * @throws IOException If a problem occurs while attempting to 594 * process the operation. 595 */ 596 private void processExtendedOperation(LDAPMessage message) 597 throws IOException 598 { 599 int messageID = message.getMessageID(); 600 ExtendedRequestProtocolOp request = 601 message.getExtendedRequestProtocolOp(); 602 if (request.getOID().equals(OID_START_TLS_REQUEST)) 603 { 604 LocalizableMessage m = ERR_INTERNALOS_STARTTLS_NOT_SUPPORTED.get(); 605 ExtendedResponseProtocolOp extendedResponse = 606 new ExtendedResponseProtocolOp( 607 LDAPResultCode.UNWILLING_TO_PERFORM, m); 608 socket.getInputStream().addLDAPMessage( 609 new LDAPMessage(messageID, extendedResponse)); 610 return; 611 } 612 613 InternalClientConnection conn = socket.getConnection(); 614 ExtendedOperationBasis op = 615 new ExtendedOperationBasis(conn, nextOperationID(), 616 messageID, message.getControls(), request.getOID(), 617 request.getValue()); 618 op.run(); 619 620 ExtendedResponseProtocolOp extendedResponse = 621 new ExtendedResponseProtocolOp( 622 op.getResultCode().intValue(), 623 op.getErrorMessage().toMessage(), 624 op.getMatchedDN(), 625 op.getReferralURLs(), op.getResponseOID(), 626 op.getResponseValue()); 627 List<Control> responseControls = op.getResponseControls(); 628 629 socket.getInputStream().addLDAPMessage( 630 new LDAPMessage(messageID, extendedResponse, 631 responseControls)); 632 } 633 634 635 636 /** 637 * Processes the content of the provided LDAP message as a modify 638 * operation and returns the appropriate result to the client. 639 * 640 * @param message The LDAP message containing the request to 641 * process. 642 * 643 * @throws IOException If a problem occurs while attempting to 644 * process the operation. 645 */ 646 private void processModifyOperation(LDAPMessage message) 647 throws IOException 648 { 649 int messageID = message.getMessageID(); 650 ModifyRequestProtocolOp request = 651 message.getModifyRequestProtocolOp(); 652 653 InternalClientConnection conn = socket.getConnection(); 654 ModifyOperationBasis op = 655 new ModifyOperationBasis(conn, nextOperationID(), 656 messageID, message.getControls(), request.getDN(), 657 request.getModifications()); 658 op.run(); 659 660 ModifyResponseProtocolOp modifyResponse = 661 new ModifyResponseProtocolOp( 662 op.getResultCode().intValue(), 663 op.getErrorMessage().toMessage(), 664 op.getMatchedDN(), 665 op.getReferralURLs()); 666 List<Control> responseControls = op.getResponseControls(); 667 668 socket.getInputStream().addLDAPMessage( 669 new LDAPMessage(messageID, modifyResponse, 670 responseControls)); 671 } 672 673 674 675 /** 676 * Processes the content of the provided LDAP message as a modify DN 677 * operation and returns the appropriate result to the client. 678 * 679 * @param message The LDAP message containing the request to 680 * process. 681 * 682 * @throws IOException If a problem occurs while attempting to 683 * process the operation. 684 */ 685 private void processModifyDNOperation(LDAPMessage message) 686 throws IOException 687 { 688 int messageID = message.getMessageID(); 689 ModifyDNRequestProtocolOp request = 690 message.getModifyDNRequestProtocolOp(); 691 692 InternalClientConnection conn = socket.getConnection(); 693 ModifyDNOperationBasis op = 694 new ModifyDNOperationBasis(conn, nextOperationID(), 695 messageID, message.getControls(), request.getEntryDN(), 696 request.getNewRDN(), request.deleteOldRDN(), 697 request.getNewSuperior()); 698 op.run(); 699 700 ModifyDNResponseProtocolOp modifyDNResponse = 701 new ModifyDNResponseProtocolOp( 702 op.getResultCode().intValue(), 703 op.getErrorMessage().toMessage(), 704 op.getMatchedDN(), 705 op.getReferralURLs()); 706 List<Control> responseControls = op.getResponseControls(); 707 708 socket.getInputStream().addLDAPMessage( 709 new LDAPMessage(messageID, modifyDNResponse, 710 responseControls)); 711 } 712 713 714 715 /** 716 * Processes the content of the provided LDAP message as a search 717 * operation and returns the appropriate result to the client. 718 * 719 * @param message The LDAP message containing the request to 720 * process. 721 * 722 * @throws IOException If a problem occurs while attempting to 723 * process the operation. 724 */ 725 private void processSearchOperation(LDAPMessage message) 726 throws IOException 727 { 728 int messageID = message.getMessageID(); 729 SearchRequestProtocolOp request = message.getSearchRequestProtocolOp(); 730 731 InternalClientConnection conn = socket.getConnection(); 732 DN baseDN = null; 733 SearchFilter filter; 734 try 735 { 736 baseDN = DN.valueOf(request.getBaseDN().toString()); 737 filter = request.getFilter().toSearchFilter(); 738 } 739 catch (DirectoryException e) 740 { 741 final String cause = baseDN == null ? "baseDN" : "filter"; 742 throw error(LocalizableMessage.raw("Could not decode " + cause), e); 743 } 744 SearchRequest sr = newSearchRequest(baseDN, request.getScope(), filter) 745 .setDereferenceAliasesPolicy(request.getDereferencePolicy()) 746 .setSizeLimit(request.getSizeLimit()) 747 .setTimeLimit(request.getTimeLimit()) 748 .setTypesOnly(request.getTypesOnly()) 749 .addAttribute(request.getAttributes()) 750 .addControl(message.getControls()); 751 InternalSearchOperation op = new InternalSearchOperation( 752 conn, nextOperationID(), messageID, sr, this); 753 op.run(); 754 755 SearchResultDoneProtocolOp searchDone = 756 new SearchResultDoneProtocolOp( 757 op.getResultCode().intValue(), 758 op.getErrorMessage().toMessage(), 759 op.getMatchedDN(), 760 op.getReferralURLs()); 761 List<Control> responseControls = op.getResponseControls(); 762 763 socket.getInputStream().addLDAPMessage( 764 new LDAPMessage(messageID, searchDone, responseControls)); 765 } 766 767 768 769 /** 770 * Performs any processing necessary for the provided search result 771 * entry. 772 * 773 * @param searchOperation The internal search operation being 774 * processed. 775 * @param searchEntry The matching search result entry to be 776 * processed. 777 */ 778 @Override 779 @org.opends.server.types.PublicAPI( 780 stability=org.opends.server.types.StabilityLevel.PRIVATE, 781 mayInstantiate=false, 782 mayExtend=false, 783 mayInvoke=false) 784 public void handleInternalSearchEntry( 785 InternalSearchOperation searchOperation, 786 SearchResultEntry searchEntry) 787 { 788 List<Control> entryControls = searchEntry.getControls(); 789 790 SearchResultEntryProtocolOp entry = 791 new SearchResultEntryProtocolOp(searchEntry); 792 793 socket.getInputStream().addLDAPMessage( 794 new LDAPMessage(searchOperation.getMessageID(), entry, entryControls)); 795 } 796 797 798 799 /** 800 * Performs any processing necessary for the provided search result reference. 801 * 802 * @param searchOperation The internal search operation being processed. 803 * @param searchReference The search result reference to be processed. 804 */ 805 @Override 806 @org.opends.server.types.PublicAPI( 807 stability=org.opends.server.types.StabilityLevel.PRIVATE, 808 mayInstantiate=false, 809 mayExtend=false, 810 mayInvoke=false) 811 public void handleInternalSearchReference( 812 InternalSearchOperation searchOperation, 813 SearchResultReference searchReference) 814 { 815 List<Control> entryControls = searchReference.getControls(); 816 817 SearchResultReferenceProtocolOp reference = 818 new SearchResultReferenceProtocolOp(searchReference); 819 820 socket.getInputStream().addLDAPMessage( 821 new LDAPMessage(searchOperation.getMessageID(), reference, 822 entryControls)); 823 } 824 825 826 827 /** 828 * Retrieves a string representation of this internal LDAP socket. 829 * 830 * @return A string representation of this internal LDAP socket. 831 */ 832 @Override 833 public String toString() 834 { 835 return getClass().getSimpleName(); 836 } 837}