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 2008 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2015 ForgeRock AS.
016 */
017package org.opends.server.protocols.internal;
018
019
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.util.concurrent.ArrayBlockingQueue;
024
025import org.forgerock.opendj.io.ASN1;
026import org.forgerock.opendj.io.ASN1Writer;
027import org.opends.server.protocols.ldap.LDAPMessage;
028import org.forgerock.opendj.ldap.ByteSequenceReader;
029import org.forgerock.opendj.ldap.ByteStringBuilder;
030
031
032/**
033 * This class provides an implementation of a
034 * {@code java.io.InputStream} that can be used to facilitate internal
035 * communication with the Directory Server.  On the backend, this
036 * input stream will be populated by ASN.1 elements encoded from LDAP
037 * messages created from internal operation responses.
038 */
039@org.opends.server.types.PublicAPI(
040     stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
041     mayInstantiate=false,
042     mayExtend=false,
043     mayInvoke=true)
044public final class InternalLDAPInputStream
045       extends InputStream
046{
047  /**
048   * The queue of LDAP messages providing the data to be made
049   * available to the client.
050   */
051  private final ArrayBlockingQueue<LDAPMessage> messageQueue;
052
053  /** Indicates whether this stream has been closed. */
054  private boolean closed;
055
056  /** The byte buffer with partial data to be written to the client. */
057  private final ByteStringBuilder messageBuffer;
058
059  /** The byte buffer reader. */
060  private final ByteSequenceReader messageReader;
061
062  /** The byte buffer writer. */
063  private final ASN1Writer writer;
064
065  /** The internal LDAP socket serviced by this input stream. */
066  private final InternalLDAPSocket socket;
067
068
069
070  /**
071   * Creates a new internal LDAP input stream that will service the
072   * provided internal LDAP socket.
073   *
074   * @param  socket  The internal LDAP socket serviced by this
075   *                 internal LDAP input stream.
076   */
077  public InternalLDAPInputStream(InternalLDAPSocket socket)
078  {
079    this.socket = socket;
080    this.messageQueue = new ArrayBlockingQueue<>(10);
081    this.messageBuffer = new ByteStringBuilder();
082    this.messageReader = messageBuffer.asReader();
083    this.writer = ASN1.getWriter(messageBuffer);
084    this.closed = false;
085  }
086
087
088
089  /**
090   * Adds the provided LDAP message to the set of messages to be
091   * returned to the client.  Note that this may block if there is
092   * already a significant backlog of messages to be returned.
093   *
094   * @param  message  The message to add to the set of messages to be
095   *                  returned to the client.
096   */
097  @org.opends.server.types.PublicAPI(
098       stability=org.opends.server.types.StabilityLevel.PRIVATE,
099       mayInstantiate=false,
100       mayExtend=false,
101       mayInvoke=false)
102  void addLDAPMessage(LDAPMessage message)
103  {
104    // If the stream is closed, then simply drop the message.
105    if (closed)
106    {
107      return;
108    }
109
110    try
111    {
112      messageQueue.put(message);
113      return;
114    }
115    catch (Exception e)
116    {
117      // This shouldn't happen, but if it does then try three more
118      // times before giving up and dropping the message.
119      for (int i=0; i < 3; i++)
120      {
121        try
122        {
123          messageQueue.put(message);
124          break;
125        } catch (Exception e2) {}
126      }
127
128      return;
129    }
130  }
131
132
133
134  /**
135   * Retrieves the number of bytes that can be read (or skipped over)
136   * from this input stream without blocking.
137   *
138   * @return  The number of bytes that can be read (or skipped over)
139   *          from this input stream without blocking.
140   * @throws IOException if an I/O error occurs.
141   */
142  @Override
143  public synchronized int available() throws IOException
144  {
145    if (messageReader.remaining() < 1)
146    {
147      LDAPMessage message = messageQueue.poll();
148      if (message == null || message instanceof NullLDAPMessage)
149      {
150        if (message != null)
151        {
152          messageQueue.clear();
153          closed = true;
154        }
155
156        return 0;
157      }
158      else
159      {
160        messageBuffer.clear();
161        messageReader.rewind();
162        message.write(writer);
163      }
164    }
165
166    return messageReader.remaining();
167  }
168
169
170
171  /**
172   * Closes this input stream.  This will add a special marker
173   * element to the message queue indicating that the end of the
174   * stream has been reached.  If the queue is full, then it will be
175   * cleared before adding the marker element.
176   */
177  @Override
178  public void close()
179  {
180    socket.close();
181  }
182
183
184
185  /**
186   * Closes this input stream through an internal mechanism that will
187   * not cause an infinite recursion loop by trying to also close the
188   * input stream.
189   */
190  @org.opends.server.types.PublicAPI(
191       stability=org.opends.server.types.StabilityLevel.PRIVATE,
192       mayInstantiate=false,
193       mayExtend=false,
194       mayInvoke=false)
195  void closeInternal()
196  {
197    if (closed)
198    {
199      return;
200    }
201
202    closed = true;
203    NullLDAPMessage nullMessage = new NullLDAPMessage();
204
205    while (! messageQueue.offer(nullMessage))
206    {
207      messageQueue.clear();
208    }
209  }
210
211
212
213  /**
214   * Marks the current position in the input stream.  This will not
215   * have any effect, as this input stream implementation does not
216   * support marking.
217   *
218   * @param  readLimit  The maximum limit of bytes that can be read
219   *                    before the mark position becomes invalid.
220   */
221  @Override
222  public void mark(int readLimit)
223  {
224    // No implementation is required.
225  }
226
227
228
229  /**
230   * Indicates whether this input stream implementation supports the
231   * use of the {@code mark} and {@code reset} methods.  This
232   * implementation does not support that functionality.
233   *
234   * @return  {@code false} because this implementation does not
235   *          support the use of the {@code mark} and {@code reset}
236   *          methods.
237   */
238  @Override
239  public boolean markSupported()
240  {
241    return false;
242  }
243
244
245
246  /**
247   * Reads the next byte of data from the input stream, blocking if
248   * necessary until there is data available.
249   *
250   * @return  The next byte of data read from the input stream, or -1
251   *          if the end of the input stream has been reached.
252   *
253   * @throws  IOException  If a problem occurs while trying to read
254   *                       data from the stream.
255   */
256  @Override
257  public synchronized int read()
258         throws IOException
259  {
260    if (messageReader.remaining() < 1)
261    {
262      LDAPMessage message;
263      try
264      {
265        message = messageQueue.take();
266      }
267      catch(InterruptedException ie)
268      {
269        // Probably because a shutdown was started. EOF
270        message = new NullLDAPMessage();
271      }
272
273      if (message == null || message instanceof NullLDAPMessage)
274      {
275        if (message instanceof NullLDAPMessage)
276        {
277          messageQueue.clear();
278          closed = true;
279          return -1;
280        }
281
282        return 0;
283      }
284      else
285      {
286        messageBuffer.clear();
287        messageReader.rewind();
288        message.write(writer);
289      }
290    }
291
292    return 0xFF & messageReader.readByte();
293  }
294
295
296
297  /**
298   * Reads some number of bytes from the input stream, blocking if
299   * necessary until there is data available, and adds them to the
300   * provided array starting at position 0.
301   *
302   * @param  b  The array to which the data is to be written.
303   *
304   * @return  The number of bytes actually written into the
305   *          provided array, or -1 if the end of the stream has been
306   *          reached.
307   *
308   * @throws  IOException  If a problem occurs while trying to read
309   *                       data from the stream.
310   */
311  @Override
312  public int read(byte[] b)
313         throws IOException
314  {
315    return read(b, 0, b.length);
316  }
317
318
319
320  /**
321   * Reads some number of bytes from the input stream, blocking if
322   * necessary until there is data available, and adds them to the
323   * provided array starting at the specified position.
324   *
325   * @param  b    The array to which the data is to be written.
326   * @param  off  The offset in the array at which to start writing
327   *              data.
328   * @param  len  The maximum number of bytes that may be added to the
329   *              array.
330   *
331   * @return  The number of bytes actually written into the
332   *          provided array, or -1 if the end of the stream has been
333   *          reached.
334   *
335   * @throws  IOException  If a problem occurs while trying to read
336   *                       data from the stream.
337   */
338  @Override
339  public synchronized int read(byte[] b, int off, int len)
340         throws IOException
341  {
342    if (messageReader.remaining() < 1)
343    {
344      LDAPMessage message;
345      try
346      {
347        message = messageQueue.take();
348      }
349      catch(InterruptedException ie)
350      {
351        // Probably because a shutdown was started. EOF
352        message = new NullLDAPMessage();
353      }
354
355      if (message == null || message instanceof NullLDAPMessage)
356      {
357        if (message instanceof NullLDAPMessage)
358        {
359          messageQueue.clear();
360          closed = true;
361          return -1;
362        }
363
364        return 0;
365      }
366      else
367      {
368        messageBuffer.clear();
369        messageReader.rewind();
370        message.write(writer);
371      }
372    }
373
374    int actualLen = Math.min(len, messageReader.remaining());
375    messageReader.readBytes(b, off, actualLen);
376    return actualLen;
377  }
378
379
380
381  /**
382   * Repositions this stream to the position at the time that the
383   * {@code mark} method was called on this stream.  This will not
384   * have any effect, as this input stream implementation does not
385   * support marking.
386   */
387  @Override
388  public void reset()
389  {
390    // No implementation is required.
391  }
392
393
394
395  /**
396   * Skips over and discards up to the specified number of bytes of
397   * data from this input stream.  This implementation will always
398   * skip the requested number of bytes unless the end of the stream
399   * is reached.
400   *
401   * @param  n  The maximum number of bytes to skip.
402   *
403   * @return  The number of bytes actually skipped.
404   *
405   * @throws  IOException  If a problem occurs while trying to read
406   *                       data from the input stream.
407   */
408  @Override
409  public synchronized long skip(long n)
410         throws IOException
411  {
412    byte[] b;
413    if (n > 8192)
414    {
415      b = new byte[8192];
416    }
417    else
418    {
419      b = new byte[(int) n];
420    }
421
422    long totalBytesRead = 0L;
423    while (totalBytesRead < n)
424    {
425      int maxLen = (int) Math.min((n - totalBytesRead), b.length);
426
427      int bytesRead = read(b, 0, maxLen);
428      if (bytesRead < 0)
429      {
430        if (totalBytesRead > 0)
431        {
432          return totalBytesRead;
433        }
434        else
435        {
436          return bytesRead;
437        }
438      }
439      else
440      {
441        totalBytesRead += bytesRead;
442      }
443    }
444
445    return totalBytesRead;
446  }
447
448
449
450  /**
451   * Retrieves a string representation of this internal LDAP socket.
452   *
453   * @return  A string representation of this internal LDAP socket.
454   */
455  @Override
456  public String toString()
457  {
458    return getClass().getSimpleName();
459  }
460}