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